OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "components/mus/ws/window_tree.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <string> | |
10 #include <vector> | |
11 | |
12 #include "base/macros.h" | |
13 #include "base/memory/ptr_util.h" | |
14 #include "base/strings/stringprintf.h" | |
15 #include "components/mus/common/types.h" | |
16 #include "components/mus/common/util.h" | |
17 #include "components/mus/public/interfaces/window_tree.mojom.h" | |
18 #include "components/mus/surfaces/surfaces_state.h" | |
19 #include "components/mus/ws/default_access_policy.h" | |
20 #include "components/mus/ws/display_binding.h" | |
21 #include "components/mus/ws/ids.h" | |
22 #include "components/mus/ws/platform_display.h" | |
23 #include "components/mus/ws/platform_display_factory.h" | |
24 #include "components/mus/ws/platform_display_init_params.h" | |
25 #include "components/mus/ws/server_window.h" | |
26 #include "components/mus/ws/server_window_surface_manager_test_api.h" | |
27 #include "components/mus/ws/test_change_tracker.h" | |
28 #include "components/mus/ws/test_server_window_delegate.h" | |
29 #include "components/mus/ws/test_utils.h" | |
30 #include "components/mus/ws/window_manager_access_policy.h" | |
31 #include "components/mus/ws/window_manager_display_root.h" | |
32 #include "components/mus/ws/window_server.h" | |
33 #include "components/mus/ws/window_server_delegate.h" | |
34 #include "components/mus/ws/window_tree_binding.h" | |
35 #include "services/shell/public/interfaces/connector.mojom.h" | |
36 #include "testing/gtest/include/gtest/gtest.h" | |
37 #include "ui/events/event.h" | |
38 #include "ui/events/event_utils.h" | |
39 #include "ui/gfx/geometry/rect.h" | |
40 | |
41 namespace mus { | |
42 namespace ws { | |
43 namespace test { | |
44 namespace { | |
45 | |
46 std::string WindowIdToString(const WindowId& id) { | |
47 return base::StringPrintf("%d,%d", id.client_id, id.window_id); | |
48 } | |
49 | |
50 ClientWindowId BuildClientWindowId(WindowTree* tree, | |
51 ClientSpecificId window_id) { | |
52 return ClientWindowId(WindowIdToTransportId(WindowId(tree->id(), window_id))); | |
53 } | |
54 | |
55 // ----------------------------------------------------------------------------- | |
56 | |
57 ui::PointerEvent CreatePointerDownEvent(int x, int y) { | |
58 return ui::PointerEvent(ui::TouchEvent(ui::ET_TOUCH_PRESSED, gfx::Point(x, y), | |
59 1, ui::EventTimeForNow())); | |
60 } | |
61 | |
62 ui::PointerEvent CreatePointerUpEvent(int x, int y) { | |
63 return ui::PointerEvent(ui::TouchEvent( | |
64 ui::ET_TOUCH_RELEASED, gfx::Point(x, y), 1, ui::EventTimeForNow())); | |
65 } | |
66 | |
67 ui::PointerEvent CreateMouseMoveEvent(int x, int y) { | |
68 return ui::PointerEvent( | |
69 ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(x, y), gfx::Point(x, y), | |
70 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE)); | |
71 } | |
72 | |
73 ui::PointerEvent CreateMouseDownEvent(int x, int y) { | |
74 return ui::PointerEvent( | |
75 ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(x, y), gfx::Point(x, y), | |
76 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, | |
77 ui::EF_LEFT_MOUSE_BUTTON)); | |
78 } | |
79 | |
80 ui::PointerEvent CreateMouseUpEvent(int x, int y) { | |
81 return ui::PointerEvent( | |
82 ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(x, y), gfx::Point(x, y), | |
83 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, | |
84 ui::EF_LEFT_MOUSE_BUTTON)); | |
85 } | |
86 | |
87 ServerWindow* GetCaptureWindow(Display* display) { | |
88 return display->GetActiveWindowManagerDisplayRoot() | |
89 ->window_manager_state() | |
90 ->capture_window(); | |
91 } | |
92 | |
93 mojom::EventMatcherPtr CreateEventMatcher(ui::mojom::EventType type) { | |
94 mojom::EventMatcherPtr matcher = mojom::EventMatcher::New(); | |
95 matcher->type_matcher = mojom::EventTypeMatcher::New(); | |
96 matcher->type_matcher->type = type; | |
97 return matcher; | |
98 } | |
99 | |
100 } // namespace | |
101 | |
102 // ----------------------------------------------------------------------------- | |
103 | |
104 class WindowTreeTest : public testing::Test { | |
105 public: | |
106 WindowTreeTest() {} | |
107 ~WindowTreeTest() override {} | |
108 | |
109 mus::mojom::Cursor cursor_id() { | |
110 return static_cast<mus::mojom::Cursor>( | |
111 window_event_targeting_helper_.cursor_id()); | |
112 } | |
113 Display* display() { return window_event_targeting_helper_.display(); } | |
114 TestWindowTreeBinding* last_binding() { | |
115 return window_event_targeting_helper_.last_binding(); | |
116 } | |
117 TestWindowTreeClient* last_window_tree_client() { | |
118 return window_event_targeting_helper_.last_window_tree_client(); | |
119 } | |
120 TestWindowTreeClient* wm_client() { | |
121 return window_event_targeting_helper_.wm_client(); | |
122 } | |
123 WindowServer* window_server() { | |
124 return window_event_targeting_helper_.window_server(); | |
125 } | |
126 WindowTree* wm_tree() { | |
127 return window_event_targeting_helper_.window_server()->GetTreeWithId(1); | |
128 } | |
129 | |
130 void DispatchEventWithoutAck(const ui::Event& event) { | |
131 DisplayTestApi(display()).OnEvent(event); | |
132 } | |
133 | |
134 void set_window_manager_internal(WindowTree* tree, | |
135 mojom::WindowManager* wm_internal) { | |
136 WindowTreeTestApi(tree).set_window_manager_internal(wm_internal); | |
137 } | |
138 | |
139 void AckPreviousEvent() { | |
140 WindowManagerStateTestApi test_api( | |
141 display()->GetActiveWindowManagerDisplayRoot()->window_manager_state()); | |
142 while (test_api.tree_awaiting_input_ack()) { | |
143 test_api.tree_awaiting_input_ack()->OnWindowInputEventAck( | |
144 0, mojom::EventResult::HANDLED); | |
145 } | |
146 } | |
147 | |
148 void DispatchEventAndAckImmediately(const ui::Event& event) { | |
149 DispatchEventWithoutAck(event); | |
150 AckPreviousEvent(); | |
151 } | |
152 | |
153 // Creates a new window from wm_tree() and embeds a new client in it. | |
154 void SetupEventTargeting(TestWindowTreeClient** out_client, | |
155 WindowTree** window_tree, | |
156 ServerWindow** window); | |
157 | |
158 // Creates a new tree as the specified user. This does what creation via | |
159 // a WindowTreeFactory does. | |
160 WindowTree* CreateNewTree(const UserId& user_id, | |
161 TestWindowTreeBinding** binding) { | |
162 WindowTree* tree = | |
163 new WindowTree(window_server(), user_id, nullptr, | |
164 base::WrapUnique(new DefaultAccessPolicy)); | |
165 *binding = new TestWindowTreeBinding(tree); | |
166 window_server()->AddTree(base::WrapUnique(tree), base::WrapUnique(*binding), | |
167 nullptr); | |
168 return tree; | |
169 } | |
170 | |
171 protected: | |
172 WindowEventTargetingHelper window_event_targeting_helper_; | |
173 | |
174 private: | |
175 DISALLOW_COPY_AND_ASSIGN(WindowTreeTest); | |
176 }; | |
177 | |
178 // Creates a new window in wm_tree(), adds it to the root, embeds a | |
179 // new client in the window and creates a child of said window. |window| is | |
180 // set to the child of |window_tree| that is created. | |
181 void WindowTreeTest::SetupEventTargeting(TestWindowTreeClient** out_client, | |
182 WindowTree** window_tree, | |
183 ServerWindow** window) { | |
184 ServerWindow* embed_window = window_event_targeting_helper_.CreatePrimaryTree( | |
185 gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 50, 50)); | |
186 window_event_targeting_helper_.CreateSecondaryTree( | |
187 embed_window, gfx::Rect(20, 20, 20, 20), out_client, window_tree, window); | |
188 } | |
189 | |
190 // Verifies focus correctly changes on pointer events. | |
191 TEST_F(WindowTreeTest, FocusOnPointer) { | |
192 const ClientWindowId embed_window_id = BuildClientWindowId(wm_tree(), 1); | |
193 EXPECT_TRUE( | |
194 wm_tree()->NewWindow(embed_window_id, ServerWindow::Properties())); | |
195 ServerWindow* embed_window = wm_tree()->GetWindowByClientId(embed_window_id); | |
196 ASSERT_TRUE(embed_window); | |
197 EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id, true)); | |
198 ASSERT_TRUE(FirstRoot(wm_tree())); | |
199 const ClientWindowId wm_root_id = FirstRootId(wm_tree()); | |
200 EXPECT_TRUE(wm_tree()->AddWindow(wm_root_id, embed_window_id)); | |
201 display()->root_window()->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
202 mojom::WindowTreeClientPtr client; | |
203 mojom::WindowTreeClientRequest client_request = GetProxy(&client); | |
204 wm_client()->Bind(std::move(client_request)); | |
205 const uint32_t embed_flags = 0; | |
206 wm_tree()->Embed(embed_window_id, std::move(client), embed_flags); | |
207 WindowTree* tree1 = window_server()->GetTreeWithRoot(embed_window); | |
208 ASSERT_TRUE(tree1 != nullptr); | |
209 ASSERT_NE(tree1, wm_tree()); | |
210 | |
211 embed_window->SetBounds(gfx::Rect(0, 0, 50, 50)); | |
212 | |
213 const ClientWindowId child1_id(BuildClientWindowId(tree1, 1)); | |
214 EXPECT_TRUE(tree1->NewWindow(child1_id, ServerWindow::Properties())); | |
215 EXPECT_TRUE(tree1->AddWindow(ClientWindowIdForWindow(tree1, embed_window), | |
216 child1_id)); | |
217 ServerWindow* child1 = tree1->GetWindowByClientId(child1_id); | |
218 ASSERT_TRUE(child1); | |
219 child1->SetVisible(true); | |
220 child1->SetBounds(gfx::Rect(20, 20, 20, 20)); | |
221 EnableHitTest(child1); | |
222 | |
223 TestWindowTreeClient* tree1_client = last_window_tree_client(); | |
224 tree1_client->tracker()->changes()->clear(); | |
225 wm_client()->tracker()->changes()->clear(); | |
226 | |
227 // Focus should not go to |child1| yet, since the parent still doesn't allow | |
228 // active children. | |
229 DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); | |
230 Display* display1 = tree1->GetDisplay(embed_window); | |
231 EXPECT_EQ(nullptr, display1->GetFocusedWindow()); | |
232 DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22)); | |
233 tree1_client->tracker()->changes()->clear(); | |
234 wm_client()->tracker()->changes()->clear(); | |
235 | |
236 display1->AddActivationParent(embed_window); | |
237 | |
238 // Focus should go to child1. This result in notifying both the window | |
239 // manager and client client being notified. | |
240 DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); | |
241 EXPECT_EQ(child1, display1->GetFocusedWindow()); | |
242 ASSERT_GE(wm_client()->tracker()->changes()->size(), 1u); | |
243 EXPECT_EQ("Focused id=2,1", | |
244 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
245 ASSERT_GE(tree1_client->tracker()->changes()->size(), 1u); | |
246 EXPECT_EQ("Focused id=2,1", | |
247 ChangesToDescription1(*tree1_client->tracker()->changes())[0]); | |
248 | |
249 DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22)); | |
250 wm_client()->tracker()->changes()->clear(); | |
251 tree1_client->tracker()->changes()->clear(); | |
252 | |
253 // Press outside of the embedded window. Note that root cannot be focused | |
254 // (because it cannot be activated). So the focus would not move in this case. | |
255 DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22)); | |
256 EXPECT_EQ(child1, display()->GetFocusedWindow()); | |
257 | |
258 DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22)); | |
259 wm_client()->tracker()->changes()->clear(); | |
260 tree1_client->tracker()->changes()->clear(); | |
261 | |
262 // Press in the same location. Should not get a focus change event (only input | |
263 // event). | |
264 DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22)); | |
265 EXPECT_EQ(child1, display()->GetFocusedWindow()); | |
266 ASSERT_EQ(wm_client()->tracker()->changes()->size(), 1u) | |
267 << SingleChangeToDescription(*wm_client()->tracker()->changes()); | |
268 EXPECT_EQ("InputEvent window=0,3 event_action=16", | |
269 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
270 EXPECT_TRUE(tree1_client->tracker()->changes()->empty()); | |
271 } | |
272 | |
273 TEST_F(WindowTreeTest, BasicInputEventTarget) { | |
274 TestWindowTreeClient* embed_client = nullptr; | |
275 WindowTree* tree = nullptr; | |
276 ServerWindow* window = nullptr; | |
277 EXPECT_NO_FATAL_FAILURE( | |
278 SetupEventTargeting(&embed_client, &tree, &window)); | |
279 | |
280 // Send an event to |v1|. |embed_client| should get the event, not | |
281 // |wm_client|, since |v1| lives inside an embedded window. | |
282 DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); | |
283 ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); | |
284 EXPECT_EQ("Focused id=2,1", | |
285 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
286 ASSERT_EQ(2u, embed_client->tracker()->changes()->size()); | |
287 EXPECT_EQ("Focused id=2,1", | |
288 ChangesToDescription1(*embed_client->tracker()->changes())[0]); | |
289 EXPECT_EQ("InputEvent window=2,1 event_action=16", | |
290 ChangesToDescription1(*embed_client->tracker()->changes())[1]); | |
291 } | |
292 | |
293 // Tests that a client can observe events outside its bounds. | |
294 TEST_F(WindowTreeTest, SetEventObserver) { | |
295 // Create an embedded client. | |
296 TestWindowTreeClient* client = nullptr; | |
297 WindowTree* tree = nullptr; | |
298 ServerWindow* window = nullptr; | |
299 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&client, &tree, &window)); | |
300 | |
301 // Create an event outside the bounds of the client. | |
302 ui::PointerEvent pointer_down = CreatePointerDownEvent(5, 5); | |
303 | |
304 // Events are not observed before setting an observer. | |
305 DispatchEventAndAckImmediately(pointer_down); | |
306 ASSERT_EQ(0u, client->tracker()->changes()->size()); | |
307 | |
308 // Create an observer for pointer-down events. | |
309 WindowTreeTestApi(tree).SetEventObserver( | |
310 CreateEventMatcher(ui::mojom::EventType::POINTER_DOWN), 111u); | |
311 | |
312 // Pointer-down events are sent to the client. | |
313 DispatchEventAndAckImmediately(pointer_down); | |
314 ASSERT_EQ(1u, client->tracker()->changes()->size()); | |
315 EXPECT_EQ("EventObserved event_action=16 event_observer_id=111", | |
316 ChangesToDescription1(*client->tracker()->changes())[0]); | |
317 client->tracker()->changes()->clear(); | |
318 | |
319 // Clearing the observer stops sending events to the client. | |
320 WindowTreeTestApi(tree).SetEventObserver(nullptr, 0u); | |
321 DispatchEventAndAckImmediately(pointer_down); | |
322 ASSERT_EQ(0u, client->tracker()->changes()->size()); | |
323 } | |
324 | |
325 // Tests that a client using an event observer does not receive events that | |
326 // don't match the EventMatcher spec. | |
327 TEST_F(WindowTreeTest, SetEventObserverNonMatching) { | |
328 // Create an embedded client. | |
329 TestWindowTreeClient* client = nullptr; | |
330 WindowTree* tree = nullptr; | |
331 ServerWindow* window = nullptr; | |
332 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&client, &tree, &window)); | |
333 | |
334 // Create an observer for pointer-down events. | |
335 WindowTreeTestApi(tree).SetEventObserver( | |
336 CreateEventMatcher(ui::mojom::EventType::POINTER_DOWN), 111u); | |
337 | |
338 // Pointer-up events are not sent to the client, since they don't match. | |
339 DispatchEventAndAckImmediately(CreatePointerUpEvent(5, 5)); | |
340 ASSERT_EQ(0u, client->tracker()->changes()->size()); | |
341 } | |
342 | |
343 // Tests that an event that both hits a client window and matches an event | |
344 // observer is sent only once to the client. | |
345 TEST_F(WindowTreeTest, SetEventObserverSendsOnce) { | |
346 // Create an embedded client. | |
347 TestWindowTreeClient* client = nullptr; | |
348 WindowTree* tree = nullptr; | |
349 ServerWindow* window = nullptr; | |
350 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&client, &tree, &window)); | |
351 | |
352 // Create an observer for pointer-up events (which do not cause focus | |
353 // changes). | |
354 WindowTreeTestApi(tree).SetEventObserver( | |
355 CreateEventMatcher(ui::mojom::EventType::POINTER_UP), 111u); | |
356 | |
357 // Create an event inside the bounds of the client. | |
358 ui::PointerEvent pointer_up = CreatePointerUpEvent(25, 25); | |
359 | |
360 // The event is dispatched once, with a flag set that it matched the event | |
361 // observer. | |
362 DispatchEventAndAckImmediately(pointer_up); | |
363 ASSERT_EQ(1u, client->tracker()->changes()->size()); | |
364 EXPECT_EQ("InputEvent window=2,1 event_action=18 event_observer_id=111", | |
365 SingleChangeToDescription(*client->tracker()->changes())); | |
366 } | |
367 | |
368 // Tests that events generated by user A are not observed by event observers for | |
369 // user B. | |
370 TEST_F(WindowTreeTest, SetEventObserverWrongUser) { | |
371 // Embed a window tree belonging to a different user. | |
372 TestWindowTreeBinding* other_binding; | |
373 WindowTree* other_tree = CreateNewTree("other_user", &other_binding); | |
374 other_binding->client()->tracker()->changes()->clear(); | |
375 | |
376 // Set event observers on both the wm tree and the other user's tree. | |
377 WindowTreeTestApi(wm_tree()).SetEventObserver( | |
378 CreateEventMatcher(ui::mojom::EventType::POINTER_UP), 111u); | |
379 WindowTreeTestApi(other_tree) | |
380 .SetEventObserver(CreateEventMatcher(ui::mojom::EventType::POINTER_UP), | |
381 222u); | |
382 | |
383 // An event is observed by the wm tree, but not by the other user's tree. | |
384 DispatchEventAndAckImmediately(CreatePointerUpEvent(5, 5)); | |
385 ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); | |
386 EXPECT_EQ("InputEvent window=0,3 event_action=18 event_observer_id=111", | |
387 SingleChangeToDescription(*wm_client()->tracker()->changes())); | |
388 ASSERT_EQ(0u, other_binding->client()->tracker()->changes()->size()); | |
389 } | |
390 | |
391 // Tests that an event observer cannot observe keystrokes. | |
392 TEST_F(WindowTreeTest, SetEventObserverKeyEventsDisallowed) { | |
393 WindowTreeTestApi(wm_tree()).SetEventObserver( | |
394 CreateEventMatcher(ui::mojom::EventType::KEY_PRESSED), 111u); | |
395 ui::KeyEvent key_pressed(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE); | |
396 DispatchEventAndAckImmediately(key_pressed); | |
397 EXPECT_EQ(0u, wm_client()->tracker()->changes()->size()); | |
398 | |
399 WindowTreeTestApi(wm_tree()).SetEventObserver( | |
400 CreateEventMatcher(ui::mojom::EventType::KEY_RELEASED), 222u); | |
401 ui::KeyEvent key_released(ui::ET_KEY_RELEASED, ui::VKEY_A, ui::EF_NONE); | |
402 DispatchEventAndAckImmediately(key_released); | |
403 EXPECT_EQ(0u, wm_client()->tracker()->changes()->size()); | |
404 } | |
405 | |
406 TEST_F(WindowTreeTest, CursorChangesWhenMouseOverWindowAndWindowSetsCursor) { | |
407 TestWindowTreeClient* embed_client = nullptr; | |
408 WindowTree* tree = nullptr; | |
409 ServerWindow* window = nullptr; | |
410 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
411 | |
412 // Like in BasicInputEventTarget, we send a pointer down event to be | |
413 // dispatched. This is only to place the mouse cursor over that window though. | |
414 DispatchEventAndAckImmediately(CreateMouseMoveEvent(21, 22)); | |
415 | |
416 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
417 | |
418 // Because the cursor is over the window when SetCursor was called, we should | |
419 // have immediately changed the cursor. | |
420 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
421 } | |
422 | |
423 TEST_F(WindowTreeTest, CursorChangesWhenEnteringWindowWithDifferentCursor) { | |
424 TestWindowTreeClient* embed_client = nullptr; | |
425 WindowTree* tree = nullptr; | |
426 ServerWindow* window = nullptr; | |
427 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
428 | |
429 // Let's create a pointer event outside the window and then move the pointer | |
430 // inside. | |
431 DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5)); | |
432 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
433 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
434 | |
435 DispatchEventAndAckImmediately(CreateMouseMoveEvent(21, 22)); | |
436 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
437 } | |
438 | |
439 TEST_F(WindowTreeTest, TouchesDontChangeCursor) { | |
440 TestWindowTreeClient* embed_client = nullptr; | |
441 WindowTree* tree = nullptr; | |
442 ServerWindow* window = nullptr; | |
443 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
444 | |
445 // Let's create a pointer event outside the window and then move the pointer | |
446 // inside. | |
447 DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5)); | |
448 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
449 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
450 | |
451 // With a touch event, we shouldn't update the cursor. | |
452 DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); | |
453 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
454 } | |
455 | |
456 TEST_F(WindowTreeTest, DragOutsideWindow) { | |
457 TestWindowTreeClient* embed_client = nullptr; | |
458 WindowTree* tree = nullptr; | |
459 ServerWindow* window = nullptr; | |
460 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
461 | |
462 // Start with the cursor outside the window. Setting the cursor shouldn't | |
463 // change the cursor. | |
464 DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5)); | |
465 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
466 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
467 | |
468 // Move the pointer to the inside of the window | |
469 DispatchEventAndAckImmediately(CreateMouseMoveEvent(21, 22)); | |
470 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
471 | |
472 // Start the drag. | |
473 DispatchEventAndAckImmediately(CreateMouseDownEvent(21, 22)); | |
474 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
475 | |
476 // Move the cursor (mouse is still down) outside the window. | |
477 DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5)); | |
478 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
479 | |
480 // Release the cursor. We should now adapt the cursor of the window | |
481 // underneath the pointer. | |
482 DispatchEventAndAckImmediately(CreateMouseUpEvent(5, 5)); | |
483 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
484 } | |
485 | |
486 TEST_F(WindowTreeTest, ChangingWindowBoundsChangesCursor) { | |
487 TestWindowTreeClient* embed_client = nullptr; | |
488 WindowTree* tree = nullptr; | |
489 ServerWindow* window = nullptr; | |
490 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
491 | |
492 // Put the cursor just outside the bounds of the window. | |
493 DispatchEventAndAckImmediately(CreateMouseMoveEvent(41, 41)); | |
494 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
495 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
496 | |
497 // Expand the bounds of the window so they now include where the cursor now | |
498 // is. | |
499 window->SetBounds(gfx::Rect(20, 20, 25, 25)); | |
500 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
501 | |
502 // Contract the bounds again. | |
503 window->SetBounds(gfx::Rect(20, 20, 20, 20)); | |
504 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
505 } | |
506 | |
507 TEST_F(WindowTreeTest, WindowReorderingChangesCursor) { | |
508 TestWindowTreeClient* embed_client = nullptr; | |
509 WindowTree* tree = nullptr; | |
510 ServerWindow* window1 = nullptr; | |
511 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window1)); | |
512 | |
513 // Create a second window right over the first. | |
514 const ClientWindowId embed_window_id(FirstRootId(tree)); | |
515 const ClientWindowId child2_id(BuildClientWindowId(tree, 2)); | |
516 EXPECT_TRUE(tree->NewWindow(child2_id, ServerWindow::Properties())); | |
517 ServerWindow* child2 = tree->GetWindowByClientId(child2_id); | |
518 ASSERT_TRUE(child2); | |
519 EXPECT_TRUE(tree->AddWindow(embed_window_id, child2_id)); | |
520 child2->SetVisible(true); | |
521 child2->SetBounds(gfx::Rect(20, 20, 20, 20)); | |
522 EnableHitTest(child2); | |
523 | |
524 // Give each window a different cursor. | |
525 window1->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
526 child2->SetPredefinedCursor(mojom::Cursor::HAND); | |
527 | |
528 // We expect window2 to be over window1 now. | |
529 DispatchEventAndAckImmediately(CreateMouseMoveEvent(22, 22)); | |
530 EXPECT_EQ(mojom::Cursor::HAND, cursor_id()); | |
531 | |
532 // But when we put window2 at the bottom, we should adapt window1's cursor. | |
533 child2->parent()->StackChildAtBottom(child2); | |
534 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
535 } | |
536 | |
537 TEST_F(WindowTreeTest, EventAck) { | |
538 const ClientWindowId embed_window_id = BuildClientWindowId(wm_tree(), 1); | |
539 EXPECT_TRUE( | |
540 wm_tree()->NewWindow(embed_window_id, ServerWindow::Properties())); | |
541 EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id, true)); | |
542 ASSERT_TRUE(FirstRoot(wm_tree())); | |
543 EXPECT_TRUE(wm_tree()->AddWindow(FirstRootId(wm_tree()), embed_window_id)); | |
544 display()->root_window()->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
545 | |
546 wm_client()->tracker()->changes()->clear(); | |
547 DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22)); | |
548 ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); | |
549 EXPECT_EQ("InputEvent window=0,3 event_action=17", | |
550 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
551 wm_client()->tracker()->changes()->clear(); | |
552 | |
553 // Send another event. This event shouldn't reach the client. | |
554 DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22)); | |
555 ASSERT_EQ(0u, wm_client()->tracker()->changes()->size()); | |
556 | |
557 // Ack the first event. That should trigger the dispatch of the second event. | |
558 AckPreviousEvent(); | |
559 ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); | |
560 EXPECT_EQ("InputEvent window=0,3 event_action=17", | |
561 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
562 } | |
563 | |
564 // Establish client, call NewTopLevelWindow(), make sure get id, and make | |
565 // sure client paused. | |
566 TEST_F(WindowTreeTest, NewTopLevelWindow) { | |
567 TestWindowManager wm_internal; | |
568 set_window_manager_internal(wm_tree(), &wm_internal); | |
569 | |
570 TestWindowTreeBinding* child_binding; | |
571 WindowTree* child_tree = CreateNewTree(wm_tree()->user_id(), &child_binding); | |
572 child_binding->client()->tracker()->changes()->clear(); | |
573 child_binding->client()->set_record_on_change_completed(true); | |
574 | |
575 // Create a new top level window. | |
576 mojo::Map<mojo::String, mojo::Array<uint8_t>> properties; | |
577 const uint32_t initial_change_id = 17; | |
578 // Explicitly use an id that does not contain the client id. | |
579 const ClientWindowId embed_window_id2_in_child(45 << 16 | 27); | |
580 static_cast<mojom::WindowTree*>(child_tree) | |
581 ->NewTopLevelWindow(initial_change_id, embed_window_id2_in_child.id, | |
582 std::move(properties)); | |
583 | |
584 // The binding should be paused until the wm acks the change. | |
585 uint32_t wm_change_id = 0u; | |
586 ASSERT_TRUE(wm_internal.did_call_create_top_level_window(&wm_change_id)); | |
587 EXPECT_TRUE(child_binding->is_paused()); | |
588 | |
589 // Create the window for |embed_window_id2_in_child|. | |
590 const ClientWindowId embed_window_id2 = BuildClientWindowId(wm_tree(), 2); | |
591 EXPECT_TRUE( | |
592 wm_tree()->NewWindow(embed_window_id2, ServerWindow::Properties())); | |
593 EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id2, true)); | |
594 EXPECT_TRUE(wm_tree()->AddWindow(FirstRootId(wm_tree()), embed_window_id2)); | |
595 | |
596 // Ack the change, which should resume the binding. | |
597 child_binding->client()->tracker()->changes()->clear(); | |
598 static_cast<mojom::WindowManagerClient*>(wm_tree()) | |
599 ->OnWmCreatedTopLevelWindow(wm_change_id, embed_window_id2.id); | |
600 EXPECT_FALSE(child_binding->is_paused()); | |
601 EXPECT_EQ("TopLevelCreated id=17 window_id=" + | |
602 WindowIdToString( | |
603 WindowIdFromTransportId(embed_window_id2_in_child.id)) + | |
604 " drawn=true", | |
605 SingleChangeToDescription( | |
606 *child_binding->client()->tracker()->changes())); | |
607 child_binding->client()->tracker()->changes()->clear(); | |
608 | |
609 // Change the visibility of the window from the owner and make sure the | |
610 // client sees the right id. | |
611 ServerWindow* embed_window = wm_tree()->GetWindowByClientId(embed_window_id2); | |
612 ASSERT_TRUE(embed_window); | |
613 EXPECT_TRUE(embed_window->visible()); | |
614 ASSERT_TRUE(wm_tree()->SetWindowVisibility( | |
615 ClientWindowIdForWindow(wm_tree(), embed_window), false)); | |
616 EXPECT_FALSE(embed_window->visible()); | |
617 EXPECT_EQ("VisibilityChanged window=" + | |
618 WindowIdToString( | |
619 WindowIdFromTransportId(embed_window_id2_in_child.id)) + | |
620 " visible=false", | |
621 SingleChangeToDescription( | |
622 *child_binding->client()->tracker()->changes())); | |
623 | |
624 // Set the visibility from the child using the client assigned id. | |
625 ASSERT_TRUE(child_tree->SetWindowVisibility(embed_window_id2_in_child, true)); | |
626 EXPECT_TRUE(embed_window->visible()); | |
627 } | |
628 | |
629 // Tests that only the capture window can release capture. | |
630 TEST_F(WindowTreeTest, ExplicitSetCapture) { | |
631 TestWindowTreeClient* embed_client = nullptr; | |
632 WindowTree* tree = nullptr; | |
633 ServerWindow* window = nullptr; | |
634 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
635 const ServerWindow* root_window = *tree->roots().begin(); | |
636 tree->AddWindow(FirstRootId(tree), ClientWindowIdForWindow(tree, window)); | |
637 window->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
638 ASSERT_TRUE(tree->GetDisplay(window)); | |
639 | |
640 // Set capture. | |
641 mojom::WindowTree* mojom_window_tree = static_cast<mojom::WindowTree*>(tree); | |
642 uint32_t change_id = 42; | |
643 mojom_window_tree->SetCapture(change_id, WindowIdToTransportId(window->id())); | |
644 Display* display = tree->GetDisplay(window); | |
645 EXPECT_EQ(window, GetCaptureWindow(display)); | |
646 | |
647 // Only the capture window should be able to release capture | |
648 mojom_window_tree->ReleaseCapture(++change_id, | |
649 WindowIdToTransportId(root_window->id())); | |
650 EXPECT_EQ(window, GetCaptureWindow(display)); | |
651 mojom_window_tree->ReleaseCapture(++change_id, | |
652 WindowIdToTransportId(window->id())); | |
653 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
654 } | |
655 | |
656 // Tests that while a client is interacting with input, that capture is not | |
657 // allowed for invisible windows. | |
658 TEST_F(WindowTreeTest, CaptureWindowMustBeVisible) { | |
659 TestWindowTreeClient* embed_client = nullptr; | |
660 WindowTree* tree = nullptr; | |
661 ServerWindow* window = nullptr; | |
662 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
663 tree->AddWindow(FirstRootId(tree), ClientWindowIdForWindow(tree, window)); | |
664 window->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
665 ASSERT_TRUE(tree->GetDisplay(window)); | |
666 | |
667 DispatchEventWithoutAck(CreatePointerDownEvent(10, 10)); | |
668 window->SetVisible(false); | |
669 EXPECT_FALSE(tree->SetCapture(ClientWindowIdForWindow(tree, window))); | |
670 EXPECT_NE(window, GetCaptureWindow(tree->GetDisplay(window))); | |
671 } | |
672 | |
673 // Tests that showing a modal window releases the capture if the capture is on a | |
674 // descendant of the modal parent. | |
675 TEST_F(WindowTreeTest, ShowModalWindowWithDescendantCapture) { | |
676 TestWindowTreeClient* embed_client = nullptr; | |
677 WindowTree* tree = nullptr; | |
678 ServerWindow* w1 = nullptr; | |
679 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
680 | |
681 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
682 const ServerWindow* root_window = *tree->roots().begin(); | |
683 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
684 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
685 Display* display = tree->GetDisplay(w1); | |
686 | |
687 // Create |w11| as a child of |w1| and make it visible. | |
688 ClientWindowId w11_id = BuildClientWindowId(tree, 11); | |
689 ASSERT_TRUE(tree->NewWindow(w11_id, ServerWindow::Properties())); | |
690 ServerWindow* w11 = tree->GetWindowByClientId(w11_id); | |
691 w11->SetBounds(gfx::Rect(10, 10, 10, 10)); | |
692 ASSERT_TRUE(tree->AddWindow(w1_id, w11_id)); | |
693 ASSERT_TRUE(tree->SetWindowVisibility(w11_id, true)); | |
694 | |
695 // Create |w2| as a child of |root_window| and modal to |w1| and leave it | |
696 // hidden. | |
697 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
698 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
699 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
700 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
701 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
702 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
703 ASSERT_TRUE(tree->SetModal(w2_id)); | |
704 | |
705 // Set capture to |w11|. | |
706 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
707 ASSERT_TRUE(tree->SetCapture(w11_id)); | |
708 EXPECT_EQ(w11, GetCaptureWindow(display)); | |
709 AckPreviousEvent(); | |
710 | |
711 // Make |w2| visible. This should release capture as capture is set to a | |
712 // descendant of the modal parent. | |
713 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
714 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
715 } | |
716 | |
717 // Tests that setting a visible window as modal releases the capture if the | |
718 // capture is on a descendant of the modal parent. | |
719 TEST_F(WindowTreeTest, VisibleWindowToModalWithDescendantCapture) { | |
720 TestWindowTreeClient* embed_client = nullptr; | |
721 WindowTree* tree = nullptr; | |
722 ServerWindow* w1 = nullptr; | |
723 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
724 | |
725 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
726 const ServerWindow* root_window = *tree->roots().begin(); | |
727 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
728 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
729 Display* display = tree->GetDisplay(w1); | |
730 | |
731 // Create |w11| as a child of |w1| and make it visible. | |
732 ClientWindowId w11_id = BuildClientWindowId(tree, 11); | |
733 ASSERT_TRUE(tree->NewWindow(w11_id, ServerWindow::Properties())); | |
734 ServerWindow* w11 = tree->GetWindowByClientId(w11_id); | |
735 w11->SetBounds(gfx::Rect(10, 10, 10, 10)); | |
736 ASSERT_TRUE(tree->AddWindow(w1_id, w11_id)); | |
737 ASSERT_TRUE(tree->SetWindowVisibility(w11_id, true)); | |
738 | |
739 // Create |w2| as a child of |root_window| and make it visible. | |
740 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
741 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
742 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
743 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
744 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
745 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
746 | |
747 // Set capture to |w11|. | |
748 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
749 ASSERT_TRUE(tree->SetCapture(w11_id)); | |
750 EXPECT_EQ(w11, GetCaptureWindow(display)); | |
751 AckPreviousEvent(); | |
752 | |
753 // Set |w2| modal to |w1|. This should release the capture as the capture is | |
754 // set to a descendant of the modal parent. | |
755 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
756 ASSERT_TRUE(tree->SetModal(w2_id)); | |
757 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
758 } | |
759 | |
760 // Tests that showing a modal window does not change capture if the capture is | |
761 // not on a descendant of the modal parent. | |
762 TEST_F(WindowTreeTest, ShowModalWindowWithNonDescendantCapture) { | |
763 TestWindowTreeClient* embed_client = nullptr; | |
764 WindowTree* tree = nullptr; | |
765 ServerWindow* w1 = nullptr; | |
766 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
767 | |
768 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
769 const ServerWindow* root_window = *tree->roots().begin(); | |
770 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
771 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
772 Display* display = tree->GetDisplay(w1); | |
773 | |
774 // Create |w2| as a child of |root_window| and modal to |w1| and leave it | |
775 // hidden.. | |
776 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
777 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
778 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
779 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
780 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
781 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
782 ASSERT_TRUE(tree->SetModal(w2_id)); | |
783 | |
784 // Create |w3| as a child of |root_window| and make it visible. | |
785 ClientWindowId w3_id = BuildClientWindowId(tree, 3); | |
786 ASSERT_TRUE(tree->NewWindow(w3_id, ServerWindow::Properties())); | |
787 ServerWindow* w3 = tree->GetWindowByClientId(w3_id); | |
788 w3->SetBounds(gfx::Rect(70, 10, 10, 10)); | |
789 ASSERT_TRUE(tree->AddWindow(root_window_id, w3_id)); | |
790 ASSERT_TRUE(tree->SetWindowVisibility(w3_id, true)); | |
791 | |
792 // Set capture to |w3|. | |
793 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
794 ASSERT_TRUE(tree->SetCapture(w3_id)); | |
795 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
796 AckPreviousEvent(); | |
797 | |
798 // Make |w2| visible. This should not change the capture as the capture is not | |
799 // set to a descendant of the modal parent. | |
800 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
801 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
802 } | |
803 | |
804 // Tests that setting a visible window as modal does not change the capture if | |
805 // the capture is not set to a descendant of the modal parent. | |
806 TEST_F(WindowTreeTest, VisibleWindowToModalWithNonDescendantCapture) { | |
807 TestWindowTreeClient* embed_client = nullptr; | |
808 WindowTree* tree = nullptr; | |
809 ServerWindow* w1 = nullptr; | |
810 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
811 | |
812 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
813 const ServerWindow* root_window = *tree->roots().begin(); | |
814 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
815 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
816 Display* display = tree->GetDisplay(w1); | |
817 | |
818 // Create |w2| and |w3| as children of |root_window| and make them visible. | |
819 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
820 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
821 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
822 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
823 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
824 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
825 | |
826 ClientWindowId w3_id = BuildClientWindowId(tree, 3); | |
827 ASSERT_TRUE(tree->NewWindow(w3_id, ServerWindow::Properties())); | |
828 ServerWindow* w3 = tree->GetWindowByClientId(w3_id); | |
829 w3->SetBounds(gfx::Rect(70, 10, 10, 10)); | |
830 ASSERT_TRUE(tree->AddWindow(root_window_id, w3_id)); | |
831 ASSERT_TRUE(tree->SetWindowVisibility(w3_id, true)); | |
832 | |
833 // Set capture to |w3|. | |
834 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
835 ASSERT_TRUE(tree->SetCapture(w3_id)); | |
836 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
837 AckPreviousEvent(); | |
838 | |
839 // Set |w2| modal to |w1|. This should not release the capture as the capture | |
840 // is not set to a descendant of the modal parent. | |
841 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
842 ASSERT_TRUE(tree->SetModal(w2_id)); | |
843 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
844 } | |
845 | |
846 // Tests that showing a system modal window releases the capture. | |
847 TEST_F(WindowTreeTest, ShowSystemModalWindowWithCapture) { | |
848 TestWindowTreeClient* embed_client = nullptr; | |
849 WindowTree* tree = nullptr; | |
850 ServerWindow* w1 = nullptr; | |
851 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
852 | |
853 w1->SetBounds(gfx::Rect(10, 10, 10, 10)); | |
854 const ServerWindow* root_window = *tree->roots().begin(); | |
855 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
856 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
857 Display* display = tree->GetDisplay(w1); | |
858 | |
859 // Create a system modal window |w2| as a child of |root_window| and leave it | |
860 // hidden. | |
861 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
862 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
863 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
864 w2->SetBounds(gfx::Rect(30, 10, 10, 10)); | |
865 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
866 ASSERT_TRUE(tree->SetModal(w2_id)); | |
867 | |
868 // Set capture to |w1|. | |
869 DispatchEventWithoutAck(CreatePointerDownEvent(15, 15)); | |
870 ASSERT_TRUE(tree->SetCapture(w1_id)); | |
871 EXPECT_EQ(w1, GetCaptureWindow(display)); | |
872 AckPreviousEvent(); | |
873 | |
874 // Make |w2| visible. This should release capture as it is system modal | |
875 // window. | |
876 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
877 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
878 } | |
879 | |
880 // Tests that setting a visible window as modal to system releases the capture. | |
881 TEST_F(WindowTreeTest, VisibleWindowToSystemModalWithCapture) { | |
882 TestWindowTreeClient* embed_client = nullptr; | |
883 WindowTree* tree = nullptr; | |
884 ServerWindow* w1 = nullptr; | |
885 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
886 | |
887 w1->SetBounds(gfx::Rect(10, 10, 10, 10)); | |
888 const ServerWindow* root_window = *tree->roots().begin(); | |
889 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
890 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
891 Display* display = tree->GetDisplay(w1); | |
892 | |
893 // Create |w2| as a child of |root_window| and make it visible. | |
894 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
895 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
896 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
897 w2->SetBounds(gfx::Rect(30, 10, 10, 10)); | |
898 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
899 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
900 | |
901 // Set capture to |w1|. | |
902 DispatchEventWithoutAck(CreatePointerDownEvent(15, 15)); | |
903 ASSERT_TRUE(tree->SetCapture(w1_id)); | |
904 EXPECT_EQ(w1, GetCaptureWindow(display)); | |
905 AckPreviousEvent(); | |
906 | |
907 // Make |w2| modal to system. This should release capture. | |
908 ASSERT_TRUE(tree->SetModal(w2_id)); | |
909 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
910 } | |
911 | |
912 // Tests that moving the capture window to a modal parent releases the capture | |
913 // as capture cannot be blocked by a modal window. | |
914 TEST_F(WindowTreeTest, MoveCaptureWindowToModalParent) { | |
915 TestWindowTreeClient* embed_client = nullptr; | |
916 WindowTree* tree = nullptr; | |
917 ServerWindow* w1 = nullptr; | |
918 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
919 | |
920 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
921 const ServerWindow* root_window = *tree->roots().begin(); | |
922 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
923 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
924 Display* display = tree->GetDisplay(w1); | |
925 | |
926 // Create |w2| and |w3| as children of |root_window| and make them visible. | |
927 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
928 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
929 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
930 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
931 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
932 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
933 | |
934 ClientWindowId w3_id = BuildClientWindowId(tree, 3); | |
935 ASSERT_TRUE(tree->NewWindow(w3_id, ServerWindow::Properties())); | |
936 ServerWindow* w3 = tree->GetWindowByClientId(w3_id); | |
937 w3->SetBounds(gfx::Rect(70, 10, 10, 10)); | |
938 ASSERT_TRUE(tree->AddWindow(root_window_id, w3_id)); | |
939 ASSERT_TRUE(tree->SetWindowVisibility(w3_id, true)); | |
940 | |
941 // Set |w2| modal to |w1|. | |
942 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
943 ASSERT_TRUE(tree->SetModal(w2_id)); | |
944 | |
945 // Set capture to |w3|. | |
946 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
947 ASSERT_TRUE(tree->SetCapture(w3_id)); | |
948 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
949 AckPreviousEvent(); | |
950 | |
951 // Make |w3| child of |w1|. This should release capture as |w3| is now blocked | |
952 // by a modal window. | |
953 ASSERT_TRUE(tree->AddWindow(w1_id, w3_id)); | |
954 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
955 } | |
956 | |
957 // Tests that opacity can be set on a known window. | |
958 TEST_F(WindowTreeTest, SetOpacity) { | |
959 TestWindowTreeClient* embed_client = nullptr; | |
960 WindowTree* tree = nullptr; | |
961 ServerWindow* window = nullptr; | |
962 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
963 | |
964 const float new_opacity = 0.5f; | |
965 EXPECT_NE(new_opacity, window->opacity()); | |
966 ASSERT_TRUE(tree->SetWindowOpacity(ClientWindowIdForWindow(tree, window), | |
967 new_opacity)); | |
968 EXPECT_EQ(new_opacity, window->opacity()); | |
969 | |
970 // Re-applying the same opacity will succeed. | |
971 EXPECT_TRUE(tree->SetWindowOpacity(ClientWindowIdForWindow(tree, window), | |
972 new_opacity)); | |
973 } | |
974 | |
975 // Tests that opacity requests for unknown windows are rejected. | |
976 TEST_F(WindowTreeTest, SetOpacityFailsOnUnknownWindow) { | |
977 TestWindowTreeClient* embed_client = nullptr; | |
978 WindowTree* tree = nullptr; | |
979 ServerWindow* window = nullptr; | |
980 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
981 | |
982 TestServerWindowDelegate delegate; | |
983 WindowId window_id(42, 1337); | |
984 ServerWindow unknown_window(&delegate, window_id); | |
985 const float new_opacity = 0.5f; | |
986 ASSERT_NE(new_opacity, unknown_window.opacity()); | |
987 | |
988 EXPECT_FALSE(tree->SetWindowOpacity( | |
989 ClientWindowId(WindowIdToTransportId(window_id)), new_opacity)); | |
990 EXPECT_NE(new_opacity, unknown_window.opacity()); | |
991 } | |
992 | |
993 TEST_F(WindowTreeTest, SetCaptureTargetsRightConnection) { | |
994 ServerWindow* window = window_event_targeting_helper_.CreatePrimaryTree( | |
995 gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 50, 50)); | |
996 WindowTree* owning_tree = | |
997 window_server()->GetTreeWithId(window->id().client_id); | |
998 WindowTree* embed_tree = window_server()->GetTreeWithRoot(window); | |
999 ASSERT_NE(owning_tree, embed_tree); | |
1000 ASSERT_TRUE( | |
1001 owning_tree->SetCapture(ClientWindowIdForWindow(owning_tree, window))); | |
1002 DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22)); | |
1003 WindowManagerStateTestApi wm_state_test_api( | |
1004 display()->GetActiveWindowManagerDisplayRoot()->window_manager_state()); | |
1005 EXPECT_EQ(owning_tree, wm_state_test_api.tree_awaiting_input_ack()); | |
1006 AckPreviousEvent(); | |
1007 | |
1008 // Set capture from the embedded client and make sure it gets the event. | |
1009 ASSERT_TRUE( | |
1010 embed_tree->SetCapture(ClientWindowIdForWindow(embed_tree, window))); | |
1011 DispatchEventWithoutAck(CreateMouseMoveEvent(22, 23)); | |
1012 EXPECT_EQ(embed_tree, wm_state_test_api.tree_awaiting_input_ack()); | |
1013 } | |
1014 | |
1015 } // namespace test | |
1016 } // namespace ws | |
1017 } // namespace mus | |
OLD | NEW |