OLD | NEW |
| (Empty) |
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 "components/mus/ws/window_manager_state.h" | |
6 | |
7 #include <memory> | |
8 | |
9 #include "base/macros.h" | |
10 #include "base/memory/ptr_util.h" | |
11 #include "base/memory/ref_counted.h" | |
12 #include "base/test/test_simple_task_runner.h" | |
13 #include "base/threading/thread_task_runner_handle.h" | |
14 #include "components/mus/common/event_matcher_util.h" | |
15 #include "components/mus/surfaces/surfaces_state.h" | |
16 #include "components/mus/ws/accelerator.h" | |
17 #include "components/mus/ws/display.h" | |
18 #include "components/mus/ws/display_binding.h" | |
19 #include "components/mus/ws/platform_display.h" | |
20 #include "components/mus/ws/platform_display_init_params.h" | |
21 #include "components/mus/ws/server_window_surface_manager_test_api.h" | |
22 #include "components/mus/ws/test_change_tracker.h" | |
23 #include "components/mus/ws/test_server_window_delegate.h" | |
24 #include "components/mus/ws/test_utils.h" | |
25 #include "components/mus/ws/window_manager_access_policy.h" | |
26 #include "components/mus/ws/window_manager_display_root.h" | |
27 #include "components/mus/ws/window_manager_state.h" | |
28 #include "components/mus/ws/window_server.h" | |
29 #include "components/mus/ws/window_tree.h" | |
30 #include "services/shell/public/interfaces/connector.mojom.h" | |
31 #include "testing/gtest/include/gtest/gtest.h" | |
32 #include "ui/events/event.h" | |
33 | |
34 namespace mus { | |
35 namespace ws { | |
36 namespace test { | |
37 | |
38 class WindowManagerStateTest : public testing::Test { | |
39 public: | |
40 WindowManagerStateTest(); | |
41 ~WindowManagerStateTest() override {} | |
42 | |
43 std::unique_ptr<Accelerator> CreateAccelerator(); | |
44 | |
45 // Creates a child |server_window| with associataed |window_tree| and | |
46 // |test_client|. The window is setup for processing input. | |
47 void CreateSecondaryTree(TestWindowTreeClient** test_client, | |
48 WindowTree** window_tree, | |
49 ServerWindow** server_window); | |
50 | |
51 void DispatchInputEventToWindow(ServerWindow* target, | |
52 const ui::Event& event, | |
53 Accelerator* accelerator); | |
54 void OnEventAckTimeout(ClientSpecificId client_id); | |
55 | |
56 WindowTree* tree() { | |
57 return window_event_targeting_helper_.window_server()->GetTreeWithId(1); | |
58 } | |
59 WindowTree* window_tree() { return window_tree_; } | |
60 TestWindowTreeClient* window_tree_client() { return window_tree_client_; } | |
61 ServerWindow* window() { return window_; } | |
62 TestWindowManager* window_manager() { return &window_manager_; } | |
63 TestWindowTreeClient* wm_client() { | |
64 return window_event_targeting_helper_.wm_client(); | |
65 } | |
66 TestWindowTreeClient* last_tree_client() { | |
67 return window_event_targeting_helper_.last_window_tree_client(); | |
68 } | |
69 WindowTree* last_tree() { | |
70 return window_event_targeting_helper_.last_binding()->tree(); | |
71 } | |
72 WindowManagerState* window_manager_state() { return window_manager_state_; } | |
73 | |
74 void EmbedAt(WindowTree* tree, | |
75 const ClientWindowId& embed_window_id, | |
76 uint32_t embed_flags, | |
77 WindowTree** embed_tree, | |
78 TestWindowTreeClient** embed_client_proxy) { | |
79 mojom::WindowTreeClientPtr embed_client; | |
80 mojom::WindowTreeClientRequest client_request = GetProxy(&embed_client); | |
81 ASSERT_TRUE( | |
82 tree->Embed(embed_window_id, std::move(embed_client), embed_flags)); | |
83 TestWindowTreeClient* client = | |
84 window_event_targeting_helper_.last_window_tree_client(); | |
85 ASSERT_EQ(1u, client->tracker()->changes()->size()); | |
86 EXPECT_EQ(CHANGE_TYPE_EMBED, (*client->tracker()->changes())[0].type); | |
87 client->tracker()->changes()->clear(); | |
88 *embed_client_proxy = client; | |
89 *embed_tree = window_event_targeting_helper_.last_binding()->tree(); | |
90 } | |
91 | |
92 // testing::Test: | |
93 void SetUp() override; | |
94 | |
95 private: | |
96 WindowEventTargetingHelper window_event_targeting_helper_; | |
97 | |
98 WindowManagerState* window_manager_state_; | |
99 | |
100 // Handles WindowStateManager ack timeouts. | |
101 scoped_refptr<base::TestSimpleTaskRunner> task_runner_; | |
102 TestWindowManager window_manager_; | |
103 ServerWindow* window_ = nullptr; | |
104 WindowTree* window_tree_ = nullptr; | |
105 TestWindowTreeClient* window_tree_client_ = nullptr; | |
106 | |
107 DISALLOW_COPY_AND_ASSIGN(WindowManagerStateTest); | |
108 }; | |
109 | |
110 WindowManagerStateTest::WindowManagerStateTest() | |
111 : task_runner_(new base::TestSimpleTaskRunner) {} | |
112 | |
113 std::unique_ptr<Accelerator> WindowManagerStateTest::CreateAccelerator() { | |
114 mojom::EventMatcherPtr matcher = mus::CreateKeyMatcher( | |
115 ui::mojom::KeyboardCode::W, ui::mojom::kEventFlagControlDown); | |
116 matcher->accelerator_phase = ui::mojom::AcceleratorPhase::POST_TARGET; | |
117 uint32_t accelerator_id = 1; | |
118 std::unique_ptr<Accelerator> accelerator( | |
119 new Accelerator(accelerator_id, *matcher)); | |
120 return accelerator; | |
121 } | |
122 | |
123 void WindowManagerStateTest::CreateSecondaryTree( | |
124 TestWindowTreeClient** test_client, | |
125 WindowTree** window_tree, | |
126 ServerWindow** server_window) { | |
127 window_event_targeting_helper_.CreateSecondaryTree( | |
128 window_, gfx::Rect(20, 20, 20, 20), test_client, window_tree, | |
129 server_window); | |
130 } | |
131 | |
132 void WindowManagerStateTest::DispatchInputEventToWindow( | |
133 ServerWindow* target, | |
134 const ui::Event& event, | |
135 Accelerator* accelerator) { | |
136 WindowManagerStateTestApi test_api(window_manager_state_); | |
137 ClientSpecificId client_id = test_api.GetEventTargetClientId(target, false); | |
138 test_api.DispatchInputEventToWindow(target, client_id, event, accelerator); | |
139 } | |
140 | |
141 void WindowManagerStateTest::OnEventAckTimeout( | |
142 ClientSpecificId client_id) { | |
143 WindowManagerStateTestApi test_api(window_manager_state_); | |
144 test_api.OnEventAckTimeout(client_id); | |
145 } | |
146 | |
147 void WindowManagerStateTest::SetUp() { | |
148 window_event_targeting_helper_.SetTaskRunner(task_runner_); | |
149 window_manager_state_ = window_event_targeting_helper_.display() | |
150 ->GetActiveWindowManagerDisplayRoot() | |
151 ->window_manager_state(); | |
152 window_ = window_event_targeting_helper_.CreatePrimaryTree( | |
153 gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 50, 50)); | |
154 window_tree_ = window_event_targeting_helper_.last_binding()->tree(); | |
155 window_tree_client_ = | |
156 window_event_targeting_helper_.last_window_tree_client(); | |
157 DCHECK(window_tree_->HasRoot(window_)); | |
158 | |
159 WindowTreeTestApi(tree()).set_window_manager_internal(&window_manager_); | |
160 wm_client()->tracker()->changes()->clear(); | |
161 window_tree_client_->tracker()->changes()->clear(); | |
162 } | |
163 | |
164 // Tests that when an event is dispatched with no accelerator, that post target | |
165 // accelerator is not triggered. | |
166 TEST_F(WindowManagerStateTest, NullAccelerator) { | |
167 WindowManagerState* state = window_manager_state(); | |
168 EXPECT_TRUE(state); | |
169 | |
170 ServerWindow* target = window(); | |
171 ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); | |
172 DispatchInputEventToWindow(target, key, nullptr); | |
173 WindowTree* target_tree = window_tree(); | |
174 TestChangeTracker* tracker = window_tree_client()->tracker(); | |
175 ASSERT_EQ(1u, tracker->changes()->size()); | |
176 EXPECT_EQ("InputEvent window=1,1 event_action=7", | |
177 ChangesToDescription1(*tracker->changes())[0]); | |
178 | |
179 WindowTreeTestApi(target_tree).AckOldestEvent(); | |
180 EXPECT_FALSE(window_manager()->on_accelerator_called()); | |
181 } | |
182 | |
183 // Tests that when a post target accelerator is provided on an event, that it is | |
184 // called on ack. | |
185 TEST_F(WindowManagerStateTest, PostTargetAccelerator) { | |
186 ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); | |
187 std::unique_ptr<Accelerator> accelerator = CreateAccelerator(); | |
188 | |
189 ServerWindow* target = window(); | |
190 DispatchInputEventToWindow(target, key, accelerator.get()); | |
191 TestChangeTracker* tracker = window_tree_client()->tracker(); | |
192 ASSERT_EQ(1u, tracker->changes()->size()); | |
193 EXPECT_EQ("InputEvent window=1,1 event_action=7", | |
194 ChangesToDescription1(*tracker->changes())[0]); | |
195 | |
196 WindowTreeTestApi(window_tree()).AckOldestEvent(); | |
197 EXPECT_TRUE(window_manager()->on_accelerator_called()); | |
198 EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id()); | |
199 } | |
200 | |
201 // Tests that when a client handles an event that post target accelerators are | |
202 // not called. | |
203 TEST_F(WindowManagerStateTest, ClientHandlesEvent) { | |
204 ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); | |
205 std::unique_ptr<Accelerator> accelerator = CreateAccelerator(); | |
206 | |
207 ServerWindow* target = window(); | |
208 DispatchInputEventToWindow(target, key, accelerator.get()); | |
209 TestChangeTracker* tracker = window_tree_client()->tracker(); | |
210 ASSERT_EQ(1u, tracker->changes()->size()); | |
211 EXPECT_EQ("InputEvent window=1,1 event_action=7", | |
212 ChangesToDescription1(*tracker->changes())[0]); | |
213 | |
214 window_manager_state()->OnEventAck(tree(), mojom::EventResult::HANDLED); | |
215 EXPECT_FALSE(window_manager()->on_accelerator_called()); | |
216 } | |
217 | |
218 // Tests that when an accelerator is deleted before an ack, that it is not | |
219 // called. | |
220 TEST_F(WindowManagerStateTest, AcceleratorDeleted) { | |
221 ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); | |
222 std::unique_ptr<Accelerator> accelerator(CreateAccelerator()); | |
223 | |
224 ServerWindow* target = window(); | |
225 DispatchInputEventToWindow(target, key, accelerator.get()); | |
226 TestChangeTracker* tracker = window_tree_client()->tracker(); | |
227 ASSERT_EQ(1u, tracker->changes()->size()); | |
228 EXPECT_EQ("InputEvent window=1,1 event_action=7", | |
229 ChangesToDescription1(*tracker->changes())[0]); | |
230 | |
231 accelerator.reset(); | |
232 window_manager_state()->OnEventAck(tree(), mojom::EventResult::UNHANDLED); | |
233 EXPECT_FALSE(window_manager()->on_accelerator_called()); | |
234 } | |
235 | |
236 // Tests that a events arriving before an ack don't notify the tree until the | |
237 // ack arrives, and that the correct accelerator is called. | |
238 TEST_F(WindowManagerStateTest, EnqueuedAccelerators) { | |
239 ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); | |
240 std::unique_ptr<Accelerator> accelerator(CreateAccelerator()); | |
241 | |
242 ServerWindow* target = window(); | |
243 DispatchInputEventToWindow(target, key, accelerator.get()); | |
244 TestChangeTracker* tracker = window_tree_client()->tracker(); | |
245 ASSERT_EQ(1u, tracker->changes()->size()); | |
246 EXPECT_EQ("InputEvent window=1,1 event_action=7", | |
247 ChangesToDescription1(*tracker->changes())[0]); | |
248 | |
249 tracker->changes()->clear(); | |
250 ui::KeyEvent key2(ui::ET_KEY_PRESSED, ui::VKEY_Y, ui::EF_CONTROL_DOWN); | |
251 mojom::EventMatcherPtr matcher = mus::CreateKeyMatcher( | |
252 ui::mojom::KeyboardCode::Y, ui::mojom::kEventFlagControlDown); | |
253 matcher->accelerator_phase = ui::mojom::AcceleratorPhase::POST_TARGET; | |
254 uint32_t accelerator_id = 2; | |
255 std::unique_ptr<Accelerator> accelerator2( | |
256 new Accelerator(accelerator_id, *matcher)); | |
257 DispatchInputEventToWindow(target, key2, accelerator2.get()); | |
258 EXPECT_TRUE(tracker->changes()->empty()); | |
259 | |
260 WindowTreeTestApi(window_tree()).AckOldestEvent(); | |
261 ASSERT_EQ(1u, tracker->changes()->size()); | |
262 EXPECT_EQ("InputEvent window=1,1 event_action=7", | |
263 ChangesToDescription1(*tracker->changes())[0]); | |
264 EXPECT_TRUE(window_manager()->on_accelerator_called()); | |
265 EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id()); | |
266 } | |
267 | |
268 // Tests that the accelerator is not sent when the tree is dying. | |
269 TEST_F(WindowManagerStateTest, DeleteTree) { | |
270 ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); | |
271 std::unique_ptr<Accelerator> accelerator = CreateAccelerator(); | |
272 | |
273 ServerWindow* target = window(); | |
274 DispatchInputEventToWindow(target, key, accelerator.get()); | |
275 TestChangeTracker* tracker = window_tree_client()->tracker(); | |
276 ASSERT_EQ(1u, tracker->changes()->size()); | |
277 EXPECT_EQ("InputEvent window=1,1 event_action=7", | |
278 ChangesToDescription1(*tracker->changes())[0]); | |
279 | |
280 window_manager_state()->OnWillDestroyTree(tree()); | |
281 EXPECT_FALSE(window_manager()->on_accelerator_called()); | |
282 } | |
283 | |
284 // Tests that if a tree is destroyed before acking, that the accelerator is | |
285 // still sent if it is not the root tree. | |
286 TEST_F(WindowManagerStateTest, DeleteNonRootTree) { | |
287 TestWindowTreeClient* embed_connection = nullptr; | |
288 WindowTree* target_tree = nullptr; | |
289 ServerWindow* target = nullptr; | |
290 CreateSecondaryTree(&embed_connection, &target_tree, &target); | |
291 TestWindowManager target_window_manager; | |
292 WindowTreeTestApi(target_tree) | |
293 .set_window_manager_internal(&target_window_manager); | |
294 | |
295 ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); | |
296 std::unique_ptr<Accelerator> accelerator = CreateAccelerator(); | |
297 DispatchInputEventToWindow(target, key, accelerator.get()); | |
298 TestChangeTracker* tracker = embed_connection->tracker(); | |
299 ASSERT_EQ(1u, tracker->changes()->size()); | |
300 EXPECT_EQ("InputEvent window=2,1 event_action=7", | |
301 ChangesToDescription1(*tracker->changes())[0]); | |
302 EXPECT_TRUE(wm_client()->tracker()->changes()->empty()); | |
303 | |
304 window_manager_state()->OnWillDestroyTree(target_tree); | |
305 EXPECT_FALSE(target_window_manager.on_accelerator_called()); | |
306 EXPECT_TRUE(window_manager()->on_accelerator_called()); | |
307 } | |
308 | |
309 // Tests that when an ack times out that the accelerator is notified. | |
310 TEST_F(WindowManagerStateTest, AckTimeout) { | |
311 ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); | |
312 std::unique_ptr<Accelerator> accelerator = CreateAccelerator(); | |
313 DispatchInputEventToWindow(window(), key, accelerator.get()); | |
314 TestChangeTracker* tracker = window_tree_client()->tracker(); | |
315 ASSERT_EQ(1u, tracker->changes()->size()); | |
316 EXPECT_EQ("InputEvent window=1,1 event_action=7", | |
317 ChangesToDescription1(*tracker->changes())[0]); | |
318 | |
319 OnEventAckTimeout(window()->id().client_id); | |
320 EXPECT_TRUE(window_manager()->on_accelerator_called()); | |
321 EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id()); | |
322 } | |
323 | |
324 TEST_F(WindowManagerStateTest, InterceptingEmbedderReceivesEvents) { | |
325 WindowTree* embedder_tree = tree(); | |
326 ServerWindow* embedder_root = window(); | |
327 const ClientWindowId embed_window_id( | |
328 WindowIdToTransportId(WindowId(embedder_tree->id(), 12))); | |
329 embedder_tree->NewWindow(embed_window_id, ServerWindow::Properties()); | |
330 ServerWindow* embedder_window = | |
331 embedder_tree->GetWindowByClientId(embed_window_id); | |
332 ASSERT_TRUE(embedder_tree->AddWindow( | |
333 ClientWindowId(WindowIdToTransportId(embedder_root->id())), | |
334 embed_window_id)); | |
335 | |
336 TestWindowTreeClient* embedder_client = wm_client(); | |
337 | |
338 { | |
339 // Do a normal embed. | |
340 const uint32_t embed_flags = 0; | |
341 WindowTree* embed_tree = nullptr; | |
342 TestWindowTreeClient* embed_client_proxy = nullptr; | |
343 EmbedAt(embedder_tree, embed_window_id, embed_flags, &embed_tree, | |
344 &embed_client_proxy); | |
345 ASSERT_TRUE(embed_client_proxy); | |
346 | |
347 // Send an event to the embed window. It should go to the embedded client. | |
348 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), | |
349 base::TimeTicks(), 0, 0); | |
350 DispatchInputEventToWindow(embedder_window, mouse, nullptr); | |
351 ASSERT_EQ(1u, embed_client_proxy->tracker()->changes()->size()); | |
352 EXPECT_EQ(CHANGE_TYPE_INPUT_EVENT, | |
353 (*embed_client_proxy->tracker()->changes())[0].type); | |
354 WindowTreeTestApi(embed_tree).AckLastEvent(mojom::EventResult::UNHANDLED); | |
355 embed_client_proxy->tracker()->changes()->clear(); | |
356 } | |
357 | |
358 { | |
359 // Do an embed where the embedder wants to intercept events to the embedded | |
360 // tree. | |
361 const uint32_t embed_flags = mojom::kEmbedFlagEmbedderInterceptsEvents; | |
362 WindowTree* embed_tree = nullptr; | |
363 TestWindowTreeClient* embed_client_proxy = nullptr; | |
364 EmbedAt(embedder_tree, embed_window_id, embed_flags, &embed_tree, | |
365 &embed_client_proxy); | |
366 ASSERT_TRUE(embed_client_proxy); | |
367 embedder_client->tracker()->changes()->clear(); | |
368 | |
369 // Send an event to the embed window. But this time, it should reach the | |
370 // embedder. | |
371 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), | |
372 base::TimeTicks(), 0, 0); | |
373 DispatchInputEventToWindow(embedder_window, mouse, nullptr); | |
374 ASSERT_EQ(0u, embed_client_proxy->tracker()->changes()->size()); | |
375 ASSERT_EQ(1u, embedder_client->tracker()->changes()->size()); | |
376 EXPECT_EQ(CHANGE_TYPE_INPUT_EVENT, | |
377 (*embedder_client->tracker()->changes())[0].type); | |
378 WindowTreeTestApi(embedder_tree) | |
379 .AckLastEvent(mojom::EventResult::UNHANDLED); | |
380 embedder_client->tracker()->changes()->clear(); | |
381 | |
382 // Embed another tree in the embedded tree. | |
383 const ClientWindowId nested_embed_window_id( | |
384 WindowIdToTransportId(WindowId(embed_tree->id(), 23))); | |
385 embed_tree->NewWindow(nested_embed_window_id, ServerWindow::Properties()); | |
386 const ClientWindowId embed_root_id( | |
387 WindowIdToTransportId((*embed_tree->roots().begin())->id())); | |
388 ASSERT_TRUE(embed_tree->AddWindow(embed_root_id, nested_embed_window_id)); | |
389 | |
390 WindowTree* nested_embed_tree = nullptr; | |
391 TestWindowTreeClient* nested_embed_client_proxy = nullptr; | |
392 EmbedAt(embed_tree, nested_embed_window_id, embed_flags, &nested_embed_tree, | |
393 &nested_embed_client_proxy); | |
394 ASSERT_TRUE(nested_embed_client_proxy); | |
395 embed_client_proxy->tracker()->changes()->clear(); | |
396 embedder_client->tracker()->changes()->clear(); | |
397 | |
398 // Send an event to the nested embed window. The event should still reach | |
399 // the outermost embedder. | |
400 ServerWindow* nested_embed_window = | |
401 embed_tree->GetWindowByClientId(nested_embed_window_id); | |
402 DCHECK(nested_embed_window->parent()); | |
403 mouse = ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), | |
404 base::TimeTicks(), 0, 0); | |
405 DispatchInputEventToWindow(nested_embed_window, mouse, nullptr); | |
406 ASSERT_EQ(0u, nested_embed_client_proxy->tracker()->changes()->size()); | |
407 ASSERT_EQ(0u, embed_client_proxy->tracker()->changes()->size()); | |
408 | |
409 ASSERT_EQ(1u, embedder_client->tracker()->changes()->size()); | |
410 EXPECT_EQ(CHANGE_TYPE_INPUT_EVENT, | |
411 (*embedder_client->tracker()->changes())[0].type); | |
412 WindowTreeTestApi(embedder_tree) | |
413 .AckLastEvent(mojom::EventResult::UNHANDLED); | |
414 } | |
415 } | |
416 | |
417 } // namespace test | |
418 } // namespace ws | |
419 } // namespace mus | |
OLD | NEW |