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 <stddef.h> | |
6 #include <stdint.h> | |
7 | |
8 #include "base/bind.h" | |
9 #include "base/logging.h" | |
10 #include "base/macros.h" | |
11 #include "base/run_loop.h" | |
12 #include "components/mus/common/util.h" | |
13 #include "components/mus/public/cpp/lib/window_private.h" | |
14 #include "components/mus/public/cpp/tests/window_server_test_base.h" | |
15 #include "components/mus/public/cpp/window_observer.h" | |
16 #include "components/mus/public/cpp/window_tree_client.h" | |
17 #include "components/mus/public/cpp/window_tree_client_delegate.h" | |
18 #include "components/mus/public/cpp/window_tree_client_observer.h" | |
19 #include "ui/gfx/geometry/rect.h" | |
20 | |
21 namespace mus { | |
22 namespace ws { | |
23 | |
24 namespace { | |
25 | |
26 Id server_id(mus::Window* window) { | |
27 return WindowPrivate(window).server_id(); | |
28 } | |
29 | |
30 mus::Window* GetChildWindowByServerId(WindowTreeClient* client, | |
31 uint32_t id) { | |
32 return client->GetWindowByServerId(id); | |
33 } | |
34 | |
35 int ValidIndexOf(const Window::Children& windows, Window* window) { | |
36 Window::Children::const_iterator it = | |
37 std::find(windows.begin(), windows.end(), window); | |
38 return (it != windows.end()) ? (it - windows.begin()) : -1; | |
39 } | |
40 | |
41 class TestWindowManagerDelegate : public WindowManagerDelegate { | |
42 public: | |
43 TestWindowManagerDelegate() {} | |
44 ~TestWindowManagerDelegate() override {} | |
45 | |
46 // WindowManagerDelegate: | |
47 void SetWindowManagerClient(WindowManagerClient* client) override {} | |
48 bool OnWmSetBounds(Window* window, gfx::Rect* bounds) override { | |
49 return false; | |
50 } | |
51 bool OnWmSetProperty( | |
52 Window* window, | |
53 const std::string& name, | |
54 std::unique_ptr<std::vector<uint8_t>>* new_data) override { | |
55 return true; | |
56 } | |
57 Window* OnWmCreateTopLevelWindow( | |
58 std::map<std::string, std::vector<uint8_t>>* properties) override { | |
59 return nullptr; | |
60 } | |
61 void OnWmClientJankinessChanged(const std::set<Window*>& client_windows, | |
62 bool janky) override {} | |
63 void OnWmNewDisplay(Window* window, | |
64 const display::Display& display) override {} | |
65 void OnAccelerator(uint32_t id, const ui::Event& event) override {} | |
66 | |
67 private: | |
68 DISALLOW_COPY_AND_ASSIGN(TestWindowManagerDelegate); | |
69 }; | |
70 | |
71 class BoundsChangeObserver : public WindowObserver { | |
72 public: | |
73 explicit BoundsChangeObserver(Window* window) : window_(window) { | |
74 window_->AddObserver(this); | |
75 } | |
76 ~BoundsChangeObserver() override { window_->RemoveObserver(this); } | |
77 | |
78 private: | |
79 // Overridden from WindowObserver: | |
80 void OnWindowBoundsChanged(Window* window, | |
81 const gfx::Rect& old_bounds, | |
82 const gfx::Rect& new_bounds) override { | |
83 DCHECK_EQ(window, window_); | |
84 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
85 } | |
86 | |
87 Window* window_; | |
88 | |
89 DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); | |
90 }; | |
91 | |
92 // Wait until the bounds of the supplied window change; returns false on | |
93 // timeout. | |
94 bool WaitForBoundsToChange(Window* window) { | |
95 BoundsChangeObserver observer(window); | |
96 return WindowServerTestBase::DoRunLoopWithTimeout(); | |
97 } | |
98 | |
99 class ClientAreaChangeObserver : public WindowObserver { | |
100 public: | |
101 explicit ClientAreaChangeObserver(Window* window) : window_(window) { | |
102 window_->AddObserver(this); | |
103 } | |
104 ~ClientAreaChangeObserver() override { window_->RemoveObserver(this); } | |
105 | |
106 private: | |
107 // Overridden from WindowObserver: | |
108 void OnWindowClientAreaChanged( | |
109 Window* window, | |
110 const gfx::Insets& old_client_area, | |
111 const std::vector<gfx::Rect>& old_additional_client_areas) override { | |
112 DCHECK_EQ(window, window_); | |
113 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
114 } | |
115 | |
116 Window* window_; | |
117 | |
118 DISALLOW_COPY_AND_ASSIGN(ClientAreaChangeObserver); | |
119 }; | |
120 | |
121 // Wait until the bounds of the supplied window change; returns false on | |
122 // timeout. | |
123 bool WaitForClientAreaToChange(Window* window) { | |
124 ClientAreaChangeObserver observer(window); | |
125 return WindowServerTestBase::DoRunLoopWithTimeout(); | |
126 } | |
127 | |
128 // Spins a run loop until the tree beginning at |root| has |tree_size| windows | |
129 // (including |root|). | |
130 class TreeSizeMatchesObserver : public WindowObserver { | |
131 public: | |
132 TreeSizeMatchesObserver(Window* tree, size_t tree_size) | |
133 : tree_(tree), tree_size_(tree_size) { | |
134 tree_->AddObserver(this); | |
135 } | |
136 ~TreeSizeMatchesObserver() override { tree_->RemoveObserver(this); } | |
137 | |
138 bool IsTreeCorrectSize() { return CountWindows(tree_) == tree_size_; } | |
139 | |
140 private: | |
141 // Overridden from WindowObserver: | |
142 void OnTreeChanged(const TreeChangeParams& params) override { | |
143 if (IsTreeCorrectSize()) | |
144 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
145 } | |
146 | |
147 size_t CountWindows(const Window* window) const { | |
148 size_t count = 1; | |
149 Window::Children::const_iterator it = window->children().begin(); | |
150 for (; it != window->children().end(); ++it) | |
151 count += CountWindows(*it); | |
152 return count; | |
153 } | |
154 | |
155 Window* tree_; | |
156 size_t tree_size_; | |
157 | |
158 DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver); | |
159 }; | |
160 | |
161 // Wait until |window| has |tree_size| descendants; returns false on timeout. | |
162 // The count includes |window|. For example, if you want to wait for |window| to | |
163 // have a single child, use a |tree_size| of 2. | |
164 bool WaitForTreeSizeToMatch(Window* window, size_t tree_size) { | |
165 TreeSizeMatchesObserver observer(window, tree_size); | |
166 return observer.IsTreeCorrectSize() || | |
167 WindowServerTestBase::DoRunLoopWithTimeout(); | |
168 } | |
169 | |
170 class OrderChangeObserver : public WindowObserver { | |
171 public: | |
172 OrderChangeObserver(Window* window) : window_(window) { | |
173 window_->AddObserver(this); | |
174 } | |
175 ~OrderChangeObserver() override { window_->RemoveObserver(this); } | |
176 | |
177 private: | |
178 // Overridden from WindowObserver: | |
179 void OnWindowReordered(Window* window, | |
180 Window* relative_window, | |
181 mojom::OrderDirection direction) override { | |
182 DCHECK_EQ(window, window_); | |
183 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
184 } | |
185 | |
186 Window* window_; | |
187 | |
188 DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); | |
189 }; | |
190 | |
191 // Wait until |window|'s tree size matches |tree_size|; returns false on | |
192 // timeout. | |
193 bool WaitForOrderChange(WindowTreeClient* client, Window* window) { | |
194 OrderChangeObserver observer(window); | |
195 return WindowServerTestBase::DoRunLoopWithTimeout(); | |
196 } | |
197 | |
198 // Tracks a window's destruction. Query is_valid() for current state. | |
199 class WindowTracker : public WindowObserver { | |
200 public: | |
201 explicit WindowTracker(Window* window) : window_(window) { | |
202 window_->AddObserver(this); | |
203 } | |
204 ~WindowTracker() override { | |
205 if (window_) | |
206 window_->RemoveObserver(this); | |
207 } | |
208 | |
209 bool is_valid() const { return !!window_; } | |
210 | |
211 private: | |
212 // Overridden from WindowObserver: | |
213 void OnWindowDestroyed(Window* window) override { | |
214 DCHECK_EQ(window, window_); | |
215 window_ = nullptr; | |
216 } | |
217 | |
218 Window* window_; | |
219 | |
220 DISALLOW_COPY_AND_ASSIGN(WindowTracker); | |
221 }; | |
222 | |
223 } // namespace | |
224 | |
225 // WindowServer | |
226 // ----------------------------------------------------------------- | |
227 | |
228 struct EmbedResult { | |
229 EmbedResult(WindowTreeClient* client, ClientSpecificId id) | |
230 : client(client), client_id(id) {} | |
231 EmbedResult() : client(nullptr), client_id(0) {} | |
232 | |
233 WindowTreeClient* client; | |
234 | |
235 // The id supplied to the callback from OnEmbed(). Depending upon the | |
236 // access policy this may or may not match the client id of | |
237 // |client|. | |
238 ClientSpecificId client_id; | |
239 }; | |
240 | |
241 Window* GetFirstRoot(WindowTreeClient* client) { | |
242 return client->GetRoots().empty() ? nullptr : *client->GetRoots().begin(); | |
243 } | |
244 | |
245 // These tests model synchronization of two peer clients of the window server, | |
246 // that are given access to some root window. | |
247 | |
248 class WindowServerTest : public WindowServerTestBase { | |
249 public: | |
250 WindowServerTest() {} | |
251 | |
252 Window* GetFirstWMRoot() { return GetFirstRoot(window_manager()); } | |
253 | |
254 Window* NewVisibleWindow(Window* parent, WindowTreeClient* client) { | |
255 Window* window = client->NewWindow(); | |
256 window->SetVisible(true); | |
257 parent->AddChild(window); | |
258 return window; | |
259 } | |
260 | |
261 // Embeds another version of the test app @ window. This runs a run loop until | |
262 // a response is received, or a timeout. On success the new WindowServer is | |
263 // returned. | |
264 EmbedResult Embed(Window* window) { | |
265 DCHECK(!embed_details_); | |
266 embed_details_.reset(new EmbedDetails); | |
267 window->Embed(ConnectAndGetWindowServerClient(), | |
268 base::Bind(&WindowServerTest::EmbedCallbackImpl, | |
269 base::Unretained(this))); | |
270 embed_details_->waiting = true; | |
271 if (!WindowServerTestBase::DoRunLoopWithTimeout()) | |
272 return EmbedResult(); | |
273 const EmbedResult result(embed_details_->client, | |
274 embed_details_->client_id); | |
275 embed_details_.reset(); | |
276 return result; | |
277 } | |
278 | |
279 // Establishes a connection to this application and asks for a | |
280 // WindowTreeClient. | |
281 mus::mojom::WindowTreeClientPtr ConnectAndGetWindowServerClient() { | |
282 mus::mojom::WindowTreeClientPtr client; | |
283 connector()->ConnectToInterface(test_name(), &client); | |
284 return client; | |
285 } | |
286 | |
287 // WindowServerTestBase: | |
288 void OnEmbed(Window* root) override { | |
289 if (!embed_details_) { | |
290 WindowServerTestBase::OnEmbed(root); | |
291 return; | |
292 } | |
293 | |
294 embed_details_->client = root->window_tree(); | |
295 if (embed_details_->callback_run) | |
296 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
297 } | |
298 | |
299 private: | |
300 // Used to track the state of a call to window->Embed(). | |
301 struct EmbedDetails { | |
302 EmbedDetails() | |
303 : callback_run(false), | |
304 result(false), | |
305 waiting(false), | |
306 client(nullptr) {} | |
307 | |
308 // The callback supplied to Embed() was received. | |
309 bool callback_run; | |
310 | |
311 // The boolean supplied to the Embed() callback. | |
312 bool result; | |
313 | |
314 // Whether a MessageLoop is running. | |
315 bool waiting; | |
316 | |
317 // Client id supplied to the Embed() callback. | |
318 ClientSpecificId client_id; | |
319 | |
320 // The WindowTreeClient that resulted from the Embed(). null if |result| is | |
321 // false. | |
322 WindowTreeClient* client; | |
323 }; | |
324 | |
325 void EmbedCallbackImpl(bool result) { | |
326 embed_details_->callback_run = true; | |
327 embed_details_->result = result; | |
328 if (embed_details_->waiting && (!result || embed_details_->client)) | |
329 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
330 } | |
331 | |
332 std::unique_ptr<EmbedDetails> embed_details_; | |
333 | |
334 DISALLOW_COPY_AND_ASSIGN(WindowServerTest); | |
335 }; | |
336 | |
337 TEST_F(WindowServerTest, RootWindow) { | |
338 ASSERT_NE(nullptr, window_manager()); | |
339 EXPECT_EQ(1u, window_manager()->GetRoots().size()); | |
340 } | |
341 | |
342 TEST_F(WindowServerTest, Embed) { | |
343 Window* window = window_manager()->NewWindow(); | |
344 ASSERT_NE(nullptr, window); | |
345 window->SetVisible(true); | |
346 GetFirstWMRoot()->AddChild(window); | |
347 WindowTreeClient* embedded = Embed(window).client; | |
348 ASSERT_NE(nullptr, embedded); | |
349 | |
350 Window* window_in_embedded = GetFirstRoot(embedded); | |
351 ASSERT_NE(nullptr, window_in_embedded); | |
352 EXPECT_EQ(server_id(window), server_id(window_in_embedded)); | |
353 EXPECT_EQ(nullptr, window_in_embedded->parent()); | |
354 EXPECT_TRUE(window_in_embedded->children().empty()); | |
355 } | |
356 | |
357 // Window manager has two windows, N1 and N11. Embeds A at N1. A should not see | |
358 // N11. | |
359 TEST_F(WindowServerTest, EmbeddedDoesntSeeChild) { | |
360 Window* window = window_manager()->NewWindow(); | |
361 ASSERT_NE(nullptr, window); | |
362 window->SetVisible(true); | |
363 GetFirstWMRoot()->AddChild(window); | |
364 Window* nested = window_manager()->NewWindow(); | |
365 ASSERT_NE(nullptr, nested); | |
366 nested->SetVisible(true); | |
367 window->AddChild(nested); | |
368 | |
369 WindowTreeClient* embedded = Embed(window).client; | |
370 ASSERT_NE(nullptr, embedded); | |
371 Window* window_in_embedded = GetFirstRoot(embedded); | |
372 EXPECT_EQ(server_id(window), server_id(window_in_embedded)); | |
373 EXPECT_EQ(nullptr, window_in_embedded->parent()); | |
374 EXPECT_TRUE(window_in_embedded->children().empty()); | |
375 } | |
376 | |
377 // TODO(beng): write a replacement test for the one that once existed here: | |
378 // This test validates the following scenario: | |
379 // - a window originating from one client | |
380 // - a window originating from a second client | |
381 // + the client originating the window is destroyed | |
382 // -> the window should still exist (since the second client is live) but | |
383 // should be disconnected from any windows. | |
384 // http://crbug.com/396300 | |
385 // | |
386 // TODO(beng): The new test should validate the scenario as described above | |
387 // except that the second client still has a valid tree. | |
388 | |
389 // Verifies that bounds changes applied to a window hierarchy in one client | |
390 // are reflected to another. | |
391 TEST_F(WindowServerTest, SetBounds) { | |
392 Window* window = window_manager()->NewWindow(); | |
393 window->SetVisible(true); | |
394 GetFirstWMRoot()->AddChild(window); | |
395 WindowTreeClient* embedded = Embed(window).client; | |
396 ASSERT_NE(nullptr, embedded); | |
397 | |
398 Window* window_in_embedded = | |
399 GetChildWindowByServerId(embedded, server_id(window)); | |
400 EXPECT_EQ(window->bounds(), window_in_embedded->bounds()); | |
401 | |
402 window->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
403 ASSERT_TRUE(WaitForBoundsToChange(window_in_embedded)); | |
404 EXPECT_TRUE(window->bounds() == window_in_embedded->bounds()); | |
405 } | |
406 | |
407 // Verifies that bounds changes applied to a window owned by a different | |
408 // client can be refused. | |
409 TEST_F(WindowServerTest, SetBoundsSecurity) { | |
410 TestWindowManagerDelegate wm_delegate; | |
411 set_window_manager_delegate(&wm_delegate); | |
412 | |
413 Window* window = window_manager()->NewWindow(); | |
414 window->SetVisible(true); | |
415 GetFirstWMRoot()->AddChild(window); | |
416 WindowTreeClient* embedded = Embed(window).client; | |
417 ASSERT_NE(nullptr, embedded); | |
418 | |
419 Window* window_in_embedded = | |
420 GetChildWindowByServerId(embedded, server_id(window)); | |
421 window->SetBounds(gfx::Rect(0, 0, 800, 600)); | |
422 ASSERT_TRUE(WaitForBoundsToChange(window_in_embedded)); | |
423 | |
424 window_in_embedded->SetBounds(gfx::Rect(0, 0, 1024, 768)); | |
425 // Bounds change is initially accepted, but the server declines the request. | |
426 EXPECT_FALSE(window->bounds() == window_in_embedded->bounds()); | |
427 | |
428 // The client is notified when the requested is declined, and updates the | |
429 // local bounds accordingly. | |
430 ASSERT_TRUE(WaitForBoundsToChange(window_in_embedded)); | |
431 EXPECT_TRUE(window->bounds() == window_in_embedded->bounds()); | |
432 set_window_manager_delegate(nullptr); | |
433 } | |
434 | |
435 // Verifies that a root window can always be destroyed. | |
436 TEST_F(WindowServerTest, DestroySecurity) { | |
437 Window* window = window_manager()->NewWindow(); | |
438 window->SetVisible(true); | |
439 GetFirstWMRoot()->AddChild(window); | |
440 | |
441 WindowTreeClient* embedded = Embed(window).client; | |
442 ASSERT_NE(nullptr, embedded); | |
443 | |
444 // The root can be destroyed, even though it was not created by the client. | |
445 Window* embed_root = GetChildWindowByServerId(embedded, server_id(window)); | |
446 WindowTracker tracker1(window); | |
447 WindowTracker tracker2(embed_root); | |
448 embed_root->Destroy(); | |
449 EXPECT_FALSE(tracker2.is_valid()); | |
450 EXPECT_TRUE(tracker1.is_valid()); | |
451 | |
452 window->Destroy(); | |
453 EXPECT_FALSE(tracker1.is_valid()); | |
454 } | |
455 | |
456 TEST_F(WindowServerTest, MultiRoots) { | |
457 Window* window1 = window_manager()->NewWindow(); | |
458 window1->SetVisible(true); | |
459 GetFirstWMRoot()->AddChild(window1); | |
460 Window* window2 = window_manager()->NewWindow(); | |
461 window2->SetVisible(true); | |
462 GetFirstWMRoot()->AddChild(window2); | |
463 WindowTreeClient* embedded1 = Embed(window1).client; | |
464 ASSERT_NE(nullptr, embedded1); | |
465 WindowTreeClient* embedded2 = Embed(window2).client; | |
466 ASSERT_NE(nullptr, embedded2); | |
467 EXPECT_NE(embedded1, embedded2); | |
468 } | |
469 | |
470 TEST_F(WindowServerTest, Reorder) { | |
471 Window* window1 = window_manager()->NewWindow(); | |
472 window1->SetVisible(true); | |
473 GetFirstWMRoot()->AddChild(window1); | |
474 | |
475 WindowTreeClient* embedded = Embed(window1).client; | |
476 ASSERT_NE(nullptr, embedded); | |
477 | |
478 Window* window11 = embedded->NewWindow(); | |
479 window11->SetVisible(true); | |
480 GetFirstRoot(embedded)->AddChild(window11); | |
481 Window* window12 = embedded->NewWindow(); | |
482 window12->SetVisible(true); | |
483 GetFirstRoot(embedded)->AddChild(window12); | |
484 ASSERT_TRUE(WaitForTreeSizeToMatch(window1, 3u)); | |
485 | |
486 Window* root_in_embedded = GetFirstRoot(embedded); | |
487 | |
488 { | |
489 window11->MoveToFront(); | |
490 // The |embedded| tree should be updated immediately. | |
491 EXPECT_EQ(root_in_embedded->children().front(), | |
492 GetChildWindowByServerId(embedded, server_id(window12))); | |
493 EXPECT_EQ(root_in_embedded->children().back(), | |
494 GetChildWindowByServerId(embedded, server_id(window11))); | |
495 | |
496 // The |window_manager()| tree is still not updated. | |
497 EXPECT_EQ(window1->children().back(), | |
498 GetChildWindowByServerId(window_manager(), server_id(window12))); | |
499 | |
500 // Wait until |window_manager()| tree is updated. | |
501 ASSERT_TRUE(WaitForOrderChange( | |
502 window_manager(), | |
503 GetChildWindowByServerId(window_manager(), server_id(window11)))); | |
504 EXPECT_EQ(window1->children().front(), | |
505 GetChildWindowByServerId(window_manager(), server_id(window12))); | |
506 EXPECT_EQ(window1->children().back(), | |
507 GetChildWindowByServerId(window_manager(), server_id(window11))); | |
508 } | |
509 | |
510 { | |
511 window11->MoveToBack(); | |
512 // |embedded| should be updated immediately. | |
513 EXPECT_EQ(root_in_embedded->children().front(), | |
514 GetChildWindowByServerId(embedded, server_id(window11))); | |
515 EXPECT_EQ(root_in_embedded->children().back(), | |
516 GetChildWindowByServerId(embedded, server_id(window12))); | |
517 | |
518 // |window_manager()| is also eventually updated. | |
519 EXPECT_EQ(window1->children().back(), | |
520 GetChildWindowByServerId(window_manager(), server_id(window11))); | |
521 ASSERT_TRUE(WaitForOrderChange( | |
522 window_manager(), | |
523 GetChildWindowByServerId(window_manager(), server_id(window11)))); | |
524 EXPECT_EQ(window1->children().front(), | |
525 GetChildWindowByServerId(window_manager(), server_id(window11))); | |
526 EXPECT_EQ(window1->children().back(), | |
527 GetChildWindowByServerId(window_manager(), server_id(window12))); | |
528 } | |
529 } | |
530 | |
531 namespace { | |
532 | |
533 class VisibilityChangeObserver : public WindowObserver { | |
534 public: | |
535 explicit VisibilityChangeObserver(Window* window) : window_(window) { | |
536 window_->AddObserver(this); | |
537 } | |
538 ~VisibilityChangeObserver() override { window_->RemoveObserver(this); } | |
539 | |
540 private: | |
541 // Overridden from WindowObserver: | |
542 void OnWindowVisibilityChanged(Window* window) override { | |
543 EXPECT_EQ(window, window_); | |
544 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
545 } | |
546 | |
547 Window* window_; | |
548 | |
549 DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver); | |
550 }; | |
551 | |
552 } // namespace | |
553 | |
554 TEST_F(WindowServerTest, Visible) { | |
555 Window* window1 = window_manager()->NewWindow(); | |
556 window1->SetVisible(true); | |
557 GetFirstWMRoot()->AddChild(window1); | |
558 | |
559 // Embed another app and verify initial state. | |
560 WindowTreeClient* embedded = Embed(window1).client; | |
561 ASSERT_NE(nullptr, embedded); | |
562 ASSERT_NE(nullptr, GetFirstRoot(embedded)); | |
563 Window* embedded_root = GetFirstRoot(embedded); | |
564 EXPECT_TRUE(embedded_root->visible()); | |
565 EXPECT_TRUE(embedded_root->IsDrawn()); | |
566 | |
567 // Change the visible state from the first client and verify its mirrored | |
568 // correctly to the embedded app. | |
569 { | |
570 VisibilityChangeObserver observer(embedded_root); | |
571 window1->SetVisible(false); | |
572 ASSERT_TRUE(WindowServerTestBase::DoRunLoopWithTimeout()); | |
573 } | |
574 | |
575 EXPECT_FALSE(window1->visible()); | |
576 EXPECT_FALSE(window1->IsDrawn()); | |
577 | |
578 EXPECT_FALSE(embedded_root->visible()); | |
579 EXPECT_FALSE(embedded_root->IsDrawn()); | |
580 | |
581 // Make the node visible again. | |
582 { | |
583 VisibilityChangeObserver observer(embedded_root); | |
584 window1->SetVisible(true); | |
585 ASSERT_TRUE(WindowServerTestBase::DoRunLoopWithTimeout()); | |
586 } | |
587 | |
588 EXPECT_TRUE(window1->visible()); | |
589 EXPECT_TRUE(window1->IsDrawn()); | |
590 | |
591 EXPECT_TRUE(embedded_root->visible()); | |
592 EXPECT_TRUE(embedded_root->IsDrawn()); | |
593 } | |
594 | |
595 namespace { | |
596 | |
597 class DrawnChangeObserver : public WindowObserver { | |
598 public: | |
599 explicit DrawnChangeObserver(Window* window) : window_(window) { | |
600 window_->AddObserver(this); | |
601 } | |
602 ~DrawnChangeObserver() override { window_->RemoveObserver(this); } | |
603 | |
604 private: | |
605 // Overridden from WindowObserver: | |
606 void OnWindowDrawnChanged(Window* window) override { | |
607 EXPECT_EQ(window, window_); | |
608 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
609 } | |
610 | |
611 Window* window_; | |
612 | |
613 DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver); | |
614 }; | |
615 | |
616 } // namespace | |
617 | |
618 TEST_F(WindowServerTest, Drawn) { | |
619 Window* window1 = window_manager()->NewWindow(); | |
620 window1->SetVisible(true); | |
621 GetFirstWMRoot()->AddChild(window1); | |
622 | |
623 // Embed another app and verify initial state. | |
624 WindowTreeClient* embedded = Embed(window1).client; | |
625 ASSERT_NE(nullptr, embedded); | |
626 ASSERT_NE(nullptr, GetFirstRoot(embedded)); | |
627 Window* embedded_root = GetFirstRoot(embedded); | |
628 EXPECT_TRUE(embedded_root->visible()); | |
629 EXPECT_TRUE(embedded_root->IsDrawn()); | |
630 | |
631 // Change the visibility of the root, this should propagate a drawn state | |
632 // change to |embedded|. | |
633 { | |
634 DrawnChangeObserver observer(embedded_root); | |
635 GetFirstWMRoot()->SetVisible(false); | |
636 ASSERT_TRUE(DoRunLoopWithTimeout()); | |
637 } | |
638 | |
639 EXPECT_TRUE(window1->visible()); | |
640 EXPECT_FALSE(window1->IsDrawn()); | |
641 | |
642 EXPECT_TRUE(embedded_root->visible()); | |
643 EXPECT_FALSE(embedded_root->IsDrawn()); | |
644 } | |
645 | |
646 // TODO(beng): tests for window event dispatcher. | |
647 // - verify that we see events for all windows. | |
648 | |
649 namespace { | |
650 | |
651 class FocusChangeObserver : public WindowObserver { | |
652 public: | |
653 explicit FocusChangeObserver(Window* window) | |
654 : window_(window), | |
655 last_gained_focus_(nullptr), | |
656 last_lost_focus_(nullptr), | |
657 quit_on_change_(true) { | |
658 window_->AddObserver(this); | |
659 } | |
660 ~FocusChangeObserver() override { window_->RemoveObserver(this); } | |
661 | |
662 void set_quit_on_change(bool value) { quit_on_change_ = value; } | |
663 | |
664 Window* last_gained_focus() { return last_gained_focus_; } | |
665 | |
666 Window* last_lost_focus() { return last_lost_focus_; } | |
667 | |
668 private: | |
669 // Overridden from WindowObserver. | |
670 void OnWindowFocusChanged(Window* gained_focus, Window* lost_focus) override { | |
671 EXPECT_TRUE(!gained_focus || gained_focus->HasFocus()); | |
672 EXPECT_FALSE(lost_focus && lost_focus->HasFocus()); | |
673 last_gained_focus_ = gained_focus; | |
674 last_lost_focus_ = lost_focus; | |
675 if (quit_on_change_) | |
676 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
677 } | |
678 | |
679 Window* window_; | |
680 Window* last_gained_focus_; | |
681 Window* last_lost_focus_; | |
682 bool quit_on_change_; | |
683 | |
684 DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver); | |
685 }; | |
686 | |
687 class NullFocusChangeObserver : public WindowTreeClientObserver { | |
688 public: | |
689 explicit NullFocusChangeObserver(WindowTreeClient* client) | |
690 : client_(client) { | |
691 client_->AddObserver(this); | |
692 } | |
693 ~NullFocusChangeObserver() override { client_->RemoveObserver(this); } | |
694 | |
695 private: | |
696 // Overridden from WindowTreeClientObserver. | |
697 void OnWindowTreeFocusChanged(Window* gained_focus, | |
698 Window* lost_focus) override { | |
699 if (!gained_focus) | |
700 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
701 } | |
702 | |
703 WindowTreeClient* client_; | |
704 | |
705 DISALLOW_COPY_AND_ASSIGN(NullFocusChangeObserver); | |
706 }; | |
707 | |
708 bool WaitForWindowToHaveFocus(Window* window) { | |
709 if (window->HasFocus()) | |
710 return true; | |
711 FocusChangeObserver observer(window); | |
712 return WindowServerTestBase::DoRunLoopWithTimeout(); | |
713 } | |
714 | |
715 bool WaitForNoWindowToHaveFocus(WindowTreeClient* client) { | |
716 if (!client->GetFocusedWindow()) | |
717 return true; | |
718 NullFocusChangeObserver observer(client); | |
719 return WindowServerTestBase::DoRunLoopWithTimeout(); | |
720 } | |
721 | |
722 } // namespace | |
723 | |
724 TEST_F(WindowServerTest, Focus) { | |
725 Window* window1 = window_manager()->NewWindow(); | |
726 window1->SetVisible(true); | |
727 GetFirstWMRoot()->AddChild(window1); | |
728 | |
729 WindowTreeClient* embedded = Embed(window1).client; | |
730 ASSERT_NE(nullptr, embedded); | |
731 Window* window11 = embedded->NewWindow(); | |
732 window11->SetVisible(true); | |
733 GetFirstRoot(embedded)->AddChild(window11); | |
734 | |
735 { | |
736 // Focus the embed root in |embedded|. | |
737 Window* embedded_root = GetFirstRoot(embedded); | |
738 FocusChangeObserver observer(embedded_root); | |
739 observer.set_quit_on_change(false); | |
740 embedded_root->SetFocus(); | |
741 ASSERT_TRUE(embedded_root->HasFocus()); | |
742 ASSERT_NE(nullptr, observer.last_gained_focus()); | |
743 EXPECT_EQ(server_id(embedded_root), | |
744 server_id(observer.last_gained_focus())); | |
745 | |
746 // |embedded_root| is the same as |window1|, make sure |window1| got | |
747 // focus too. | |
748 ASSERT_TRUE(WaitForWindowToHaveFocus(window1)); | |
749 } | |
750 | |
751 // Focus a child of GetFirstRoot(embedded). | |
752 { | |
753 FocusChangeObserver observer(window11); | |
754 observer.set_quit_on_change(false); | |
755 window11->SetFocus(); | |
756 ASSERT_TRUE(window11->HasFocus()); | |
757 ASSERT_NE(nullptr, observer.last_gained_focus()); | |
758 ASSERT_NE(nullptr, observer.last_lost_focus()); | |
759 EXPECT_EQ(server_id(window11), server_id(observer.last_gained_focus())); | |
760 EXPECT_EQ(server_id(GetFirstRoot(embedded)), | |
761 server_id(observer.last_lost_focus())); | |
762 } | |
763 | |
764 { | |
765 // Add an observer on the Window that loses focus, and make sure the | |
766 // observer sees the right values. | |
767 FocusChangeObserver observer(window11); | |
768 observer.set_quit_on_change(false); | |
769 GetFirstRoot(embedded)->SetFocus(); | |
770 ASSERT_NE(nullptr, observer.last_gained_focus()); | |
771 ASSERT_NE(nullptr, observer.last_lost_focus()); | |
772 EXPECT_EQ(server_id(window11), server_id(observer.last_lost_focus())); | |
773 EXPECT_EQ(server_id(GetFirstRoot(embedded)), | |
774 server_id(observer.last_gained_focus())); | |
775 } | |
776 } | |
777 | |
778 TEST_F(WindowServerTest, ClearFocus) { | |
779 Window* window1 = window_manager()->NewWindow(); | |
780 window1->SetVisible(true); | |
781 GetFirstWMRoot()->AddChild(window1); | |
782 | |
783 WindowTreeClient* embedded = Embed(window1).client; | |
784 ASSERT_NE(nullptr, embedded); | |
785 Window* window11 = embedded->NewWindow(); | |
786 window11->SetVisible(true); | |
787 GetFirstRoot(embedded)->AddChild(window11); | |
788 | |
789 // Focus the embed root in |embedded|. | |
790 Window* embedded_root = GetFirstRoot(embedded); | |
791 { | |
792 FocusChangeObserver observer(embedded_root); | |
793 observer.set_quit_on_change(false); | |
794 embedded_root->SetFocus(); | |
795 ASSERT_TRUE(embedded_root->HasFocus()); | |
796 ASSERT_NE(nullptr, observer.last_gained_focus()); | |
797 EXPECT_EQ(server_id(embedded_root), | |
798 server_id(observer.last_gained_focus())); | |
799 | |
800 // |embedded_root| is the same as |window1|, make sure |window1| got | |
801 // focus too. | |
802 ASSERT_TRUE(WaitForWindowToHaveFocus(window1)); | |
803 } | |
804 | |
805 { | |
806 FocusChangeObserver observer(window1); | |
807 embedded->ClearFocus(); | |
808 ASSERT_FALSE(embedded_root->HasFocus()); | |
809 EXPECT_FALSE(embedded->GetFocusedWindow()); | |
810 | |
811 ASSERT_TRUE(WindowServerTestBase::DoRunLoopWithTimeout()); | |
812 EXPECT_FALSE(window1->HasFocus()); | |
813 EXPECT_FALSE(window_manager()->GetFocusedWindow()); | |
814 } | |
815 } | |
816 | |
817 TEST_F(WindowServerTest, FocusNonFocusableWindow) { | |
818 Window* window = window_manager()->NewWindow(); | |
819 window->SetVisible(true); | |
820 GetFirstWMRoot()->AddChild(window); | |
821 | |
822 WindowTreeClient* client = Embed(window).client; | |
823 ASSERT_NE(nullptr, client); | |
824 ASSERT_FALSE(client->GetRoots().empty()); | |
825 Window* client_window = *client->GetRoots().begin(); | |
826 client_window->SetCanFocus(false); | |
827 | |
828 client_window->SetFocus(); | |
829 ASSERT_TRUE(client_window->HasFocus()); | |
830 | |
831 WaitForNoWindowToHaveFocus(client); | |
832 ASSERT_FALSE(client_window->HasFocus()); | |
833 } | |
834 | |
835 TEST_F(WindowServerTest, Activation) { | |
836 Window* parent = NewVisibleWindow(GetFirstWMRoot(), window_manager()); | |
837 | |
838 // Allow the child windows to be activated. Do this before we wait, that way | |
839 // we're guaranteed that when we request focus from a separate client the | |
840 // requests are processed in order. | |
841 window_manager_client()->AddActivationParent(parent); | |
842 | |
843 Window* child1 = NewVisibleWindow(parent, window_manager()); | |
844 Window* child2 = NewVisibleWindow(parent, window_manager()); | |
845 Window* child3 = NewVisibleWindow(parent, window_manager()); | |
846 | |
847 child1->AddTransientWindow(child3); | |
848 | |
849 WindowTreeClient* embedded1 = Embed(child1).client; | |
850 ASSERT_NE(nullptr, embedded1); | |
851 WindowTreeClient* embedded2 = Embed(child2).client; | |
852 ASSERT_NE(nullptr, embedded2); | |
853 | |
854 Window* child11 = NewVisibleWindow(GetFirstRoot(embedded1), embedded1); | |
855 Window* child21 = NewVisibleWindow(GetFirstRoot(embedded2), embedded2); | |
856 | |
857 WaitForTreeSizeToMatch(parent, 6); | |
858 | |
859 // |child2| and |child3| are stacked about |child1|. | |
860 EXPECT_GT(ValidIndexOf(parent->children(), child2), | |
861 ValidIndexOf(parent->children(), child1)); | |
862 EXPECT_GT(ValidIndexOf(parent->children(), child3), | |
863 ValidIndexOf(parent->children(), child1)); | |
864 | |
865 // Set focus on |child11|. This should activate |child1|, and raise it over | |
866 // |child2|. But |child3| should still be above |child1| because of | |
867 // transiency. | |
868 child11->SetFocus(); | |
869 ASSERT_TRUE(WaitForWindowToHaveFocus(child11)); | |
870 ASSERT_TRUE(WaitForWindowToHaveFocus( | |
871 GetChildWindowByServerId(window_manager(), server_id(child11)))); | |
872 EXPECT_EQ(server_id(child11), | |
873 server_id(window_manager()->GetFocusedWindow())); | |
874 EXPECT_EQ(server_id(child11), server_id(embedded1->GetFocusedWindow())); | |
875 EXPECT_EQ(nullptr, embedded2->GetFocusedWindow()); | |
876 EXPECT_GT(ValidIndexOf(parent->children(), child1), | |
877 ValidIndexOf(parent->children(), child2)); | |
878 EXPECT_GT(ValidIndexOf(parent->children(), child3), | |
879 ValidIndexOf(parent->children(), child1)); | |
880 | |
881 // Set focus on |child21|. This should activate |child2|, and raise it over | |
882 // |child1|. | |
883 child21->SetFocus(); | |
884 ASSERT_TRUE(WaitForWindowToHaveFocus(child21)); | |
885 ASSERT_TRUE(WaitForWindowToHaveFocus( | |
886 GetChildWindowByServerId(window_manager(), server_id(child21)))); | |
887 EXPECT_EQ(server_id(child21), | |
888 server_id(window_manager()->GetFocusedWindow())); | |
889 EXPECT_EQ(server_id(child21), server_id(embedded2->GetFocusedWindow())); | |
890 EXPECT_TRUE(WaitForNoWindowToHaveFocus(embedded1)); | |
891 EXPECT_EQ(nullptr, embedded1->GetFocusedWindow()); | |
892 EXPECT_GT(ValidIndexOf(parent->children(), child2), | |
893 ValidIndexOf(parent->children(), child1)); | |
894 EXPECT_GT(ValidIndexOf(parent->children(), child3), | |
895 ValidIndexOf(parent->children(), child1)); | |
896 } | |
897 | |
898 TEST_F(WindowServerTest, ActivationNext) { | |
899 Window* parent = GetFirstWMRoot(); | |
900 Window* child1 = NewVisibleWindow(parent, window_manager()); | |
901 Window* child2 = NewVisibleWindow(parent, window_manager()); | |
902 Window* child3 = NewVisibleWindow(parent, window_manager()); | |
903 | |
904 WindowTreeClient* embedded1 = Embed(child1).client; | |
905 ASSERT_NE(nullptr, embedded1); | |
906 WindowTreeClient* embedded2 = Embed(child2).client; | |
907 ASSERT_NE(nullptr, embedded2); | |
908 WindowTreeClient* embedded3 = Embed(child3).client; | |
909 ASSERT_NE(nullptr, embedded3); | |
910 | |
911 Window* child11 = NewVisibleWindow(GetFirstRoot(embedded1), embedded1); | |
912 Window* child21 = NewVisibleWindow(GetFirstRoot(embedded2), embedded2); | |
913 Window* child31 = NewVisibleWindow(GetFirstRoot(embedded3), embedded3); | |
914 WaitForTreeSizeToMatch(parent, 7); | |
915 | |
916 Window* expecteds[] = { child3, child2, child1, child3, nullptr }; | |
917 Window* focused[] = { child31, child21, child11, child31, nullptr }; | |
918 for (size_t index = 0; expecteds[index]; ++index) { | |
919 window_manager_client()->ActivateNextWindow(); | |
920 WaitForWindowToHaveFocus(focused[index]); | |
921 EXPECT_TRUE(focused[index]->HasFocus()); | |
922 EXPECT_EQ(parent->children().back(), expecteds[index]); | |
923 } | |
924 } | |
925 | |
926 namespace { | |
927 | |
928 class DestroyedChangedObserver : public WindowObserver { | |
929 public: | |
930 DestroyedChangedObserver(WindowServerTestBase* test, | |
931 Window* window, | |
932 bool* got_destroy) | |
933 : test_(test), window_(window), got_destroy_(got_destroy) { | |
934 window_->AddObserver(this); | |
935 } | |
936 ~DestroyedChangedObserver() override { | |
937 if (window_) | |
938 window_->RemoveObserver(this); | |
939 } | |
940 | |
941 private: | |
942 // Overridden from WindowObserver: | |
943 void OnWindowDestroyed(Window* window) override { | |
944 EXPECT_EQ(window, window_); | |
945 window_->RemoveObserver(this); | |
946 *got_destroy_ = true; | |
947 window_ = nullptr; | |
948 | |
949 // We should always get OnWindowDestroyed() before | |
950 // OnDidDestroyClient(). | |
951 EXPECT_FALSE(test_->window_tree_client_destroyed()); | |
952 } | |
953 | |
954 WindowServerTestBase* test_; | |
955 Window* window_; | |
956 bool* got_destroy_; | |
957 | |
958 DISALLOW_COPY_AND_ASSIGN(DestroyedChangedObserver); | |
959 }; | |
960 | |
961 } // namespace | |
962 | |
963 // Verifies deleting a WindowServer sends the right notifications. | |
964 TEST_F(WindowServerTest, DeleteWindowServer) { | |
965 Window* window = window_manager()->NewWindow(); | |
966 ASSERT_NE(nullptr, window); | |
967 window->SetVisible(true); | |
968 GetFirstWMRoot()->AddChild(window); | |
969 WindowTreeClient* client = Embed(window).client; | |
970 ASSERT_TRUE(client); | |
971 bool got_destroy = false; | |
972 DestroyedChangedObserver observer(this, GetFirstRoot(client), | |
973 &got_destroy); | |
974 delete client; | |
975 EXPECT_TRUE(window_tree_client_destroyed()); | |
976 EXPECT_TRUE(got_destroy); | |
977 } | |
978 | |
979 // Verifies two Embed()s in the same window trigger deletion of the first | |
980 // WindowServer. | |
981 TEST_F(WindowServerTest, DisconnectTriggersDelete) { | |
982 Window* window = window_manager()->NewWindow(); | |
983 ASSERT_NE(nullptr, window); | |
984 window->SetVisible(true); | |
985 GetFirstWMRoot()->AddChild(window); | |
986 WindowTreeClient* client = Embed(window).client; | |
987 EXPECT_NE(client, window_manager()); | |
988 Window* embedded_window = client->NewWindow(); | |
989 // Embed again, this should trigger disconnect and deletion of client. | |
990 bool got_destroy; | |
991 DestroyedChangedObserver observer(this, embedded_window, &got_destroy); | |
992 EXPECT_FALSE(window_tree_client_destroyed()); | |
993 Embed(window); | |
994 EXPECT_TRUE(window_tree_client_destroyed()); | |
995 } | |
996 | |
997 class WindowRemovedFromParentObserver : public WindowObserver { | |
998 public: | |
999 explicit WindowRemovedFromParentObserver(Window* window) | |
1000 : window_(window), was_removed_(false) { | |
1001 window_->AddObserver(this); | |
1002 } | |
1003 ~WindowRemovedFromParentObserver() override { window_->RemoveObserver(this); } | |
1004 | |
1005 bool was_removed() const { return was_removed_; } | |
1006 | |
1007 private: | |
1008 // Overridden from WindowObserver: | |
1009 void OnTreeChanged(const TreeChangeParams& params) override { | |
1010 if (params.target == window_ && !params.new_parent) | |
1011 was_removed_ = true; | |
1012 } | |
1013 | |
1014 Window* window_; | |
1015 bool was_removed_; | |
1016 | |
1017 DISALLOW_COPY_AND_ASSIGN(WindowRemovedFromParentObserver); | |
1018 }; | |
1019 | |
1020 TEST_F(WindowServerTest, EmbedRemovesChildren) { | |
1021 Window* window1 = window_manager()->NewWindow(); | |
1022 Window* window2 = window_manager()->NewWindow(); | |
1023 GetFirstWMRoot()->AddChild(window1); | |
1024 window1->AddChild(window2); | |
1025 | |
1026 WindowRemovedFromParentObserver observer(window2); | |
1027 window1->Embed(ConnectAndGetWindowServerClient()); | |
1028 EXPECT_TRUE(observer.was_removed()); | |
1029 EXPECT_EQ(nullptr, window2->parent()); | |
1030 EXPECT_TRUE(window1->children().empty()); | |
1031 | |
1032 // Run the message loop so the Embed() call above completes. Without this | |
1033 // we may end up reconnecting to the test and rerunning the test, which is | |
1034 // problematic since the other services don't shut down. | |
1035 ASSERT_TRUE(DoRunLoopWithTimeout()); | |
1036 } | |
1037 | |
1038 namespace { | |
1039 | |
1040 class DestroyObserver : public WindowObserver { | |
1041 public: | |
1042 DestroyObserver(WindowServerTestBase* test, | |
1043 WindowTreeClient* client, | |
1044 bool* got_destroy) | |
1045 : test_(test), got_destroy_(got_destroy) { | |
1046 GetFirstRoot(client)->AddObserver(this); | |
1047 } | |
1048 ~DestroyObserver() override {} | |
1049 | |
1050 private: | |
1051 // Overridden from WindowObserver: | |
1052 void OnWindowDestroyed(Window* window) override { | |
1053 *got_destroy_ = true; | |
1054 window->RemoveObserver(this); | |
1055 | |
1056 // We should always get OnWindowDestroyed() before | |
1057 // OnWindowManagerDestroyed(). | |
1058 EXPECT_FALSE(test_->window_tree_client_destroyed()); | |
1059 | |
1060 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop()); | |
1061 } | |
1062 | |
1063 WindowServerTestBase* test_; | |
1064 bool* got_destroy_; | |
1065 | |
1066 DISALLOW_COPY_AND_ASSIGN(DestroyObserver); | |
1067 }; | |
1068 | |
1069 } // namespace | |
1070 | |
1071 // Verifies deleting a Window that is the root of another client notifies | |
1072 // observers in the right order (OnWindowDestroyed() before | |
1073 // OnWindowManagerDestroyed()). | |
1074 TEST_F(WindowServerTest, WindowServerDestroyedAfterRootObserver) { | |
1075 Window* embed_window = window_manager()->NewWindow(); | |
1076 GetFirstWMRoot()->AddChild(embed_window); | |
1077 | |
1078 WindowTreeClient* embedded_client = Embed(embed_window).client; | |
1079 | |
1080 bool got_destroy = false; | |
1081 DestroyObserver observer(this, embedded_client, &got_destroy); | |
1082 // Delete the window |embedded_client| is embedded in. This is async, | |
1083 // but will eventually trigger deleting |embedded_client|. | |
1084 embed_window->Destroy(); | |
1085 EXPECT_TRUE(DoRunLoopWithTimeout()); | |
1086 EXPECT_TRUE(got_destroy); | |
1087 } | |
1088 | |
1089 TEST_F(WindowServerTest, ClientAreaChanged) { | |
1090 Window* embed_window = window_manager()->NewWindow(); | |
1091 GetFirstWMRoot()->AddChild(embed_window); | |
1092 | |
1093 WindowTreeClient* embedded_client = Embed(embed_window).client; | |
1094 | |
1095 // Verify change from embedded makes it to parent. | |
1096 GetFirstRoot(embedded_client)->SetClientArea(gfx::Insets(1, 2, 3, 4)); | |
1097 ASSERT_TRUE(WaitForClientAreaToChange(embed_window)); | |
1098 EXPECT_TRUE(gfx::Insets(1, 2, 3, 4) == embed_window->client_area()); | |
1099 | |
1100 // Changing bounds shouldn't effect client area. | |
1101 embed_window->SetBounds(gfx::Rect(21, 22, 23, 24)); | |
1102 WaitForBoundsToChange(GetFirstRoot(embedded_client)); | |
1103 EXPECT_TRUE(gfx::Rect(21, 22, 23, 24) == | |
1104 GetFirstRoot(embedded_client)->bounds()); | |
1105 EXPECT_TRUE(gfx::Insets(1, 2, 3, 4) == | |
1106 GetFirstRoot(embedded_client)->client_area()); | |
1107 } | |
1108 | |
1109 class EstablishConnectionViaFactoryDelegate : public TestWindowManagerDelegate { | |
1110 public: | |
1111 explicit EstablishConnectionViaFactoryDelegate( | |
1112 WindowTreeClient* client) | |
1113 : client_(client), run_loop_(nullptr), created_window_(nullptr) {} | |
1114 ~EstablishConnectionViaFactoryDelegate() override {} | |
1115 | |
1116 bool QuitOnCreate() { | |
1117 if (run_loop_) | |
1118 return false; | |
1119 | |
1120 created_window_ = nullptr; | |
1121 run_loop_.reset(new base::RunLoop); | |
1122 run_loop_->Run(); | |
1123 run_loop_.reset(); | |
1124 return created_window_ != nullptr; | |
1125 } | |
1126 | |
1127 Window* created_window() { return created_window_; } | |
1128 | |
1129 // WindowManagerDelegate: | |
1130 Window* OnWmCreateTopLevelWindow( | |
1131 std::map<std::string, std::vector<uint8_t>>* properties) override { | |
1132 created_window_ = client_->NewWindow(properties); | |
1133 (*client_->GetRoots().begin())->AddChild(created_window_); | |
1134 if (run_loop_) | |
1135 run_loop_->Quit(); | |
1136 return created_window_; | |
1137 } | |
1138 | |
1139 private: | |
1140 WindowTreeClient* client_; | |
1141 std::unique_ptr<base::RunLoop> run_loop_; | |
1142 Window* created_window_; | |
1143 | |
1144 DISALLOW_COPY_AND_ASSIGN(EstablishConnectionViaFactoryDelegate); | |
1145 }; | |
1146 | |
1147 TEST_F(WindowServerTest, EstablishConnectionViaFactory) { | |
1148 EstablishConnectionViaFactoryDelegate delegate(window_manager()); | |
1149 set_window_manager_delegate(&delegate); | |
1150 std::unique_ptr<WindowTreeClient> second_client( | |
1151 new WindowTreeClient(this, nullptr, nullptr)); | |
1152 second_client->ConnectViaWindowTreeFactory(connector()); | |
1153 Window* window_in_second_client = | |
1154 second_client->NewTopLevelWindow(nullptr); | |
1155 ASSERT_TRUE(window_in_second_client); | |
1156 ASSERT_TRUE(second_client->GetRoots().count(window_in_second_client) > | |
1157 0); | |
1158 // Wait for the window to appear in the wm. | |
1159 ASSERT_TRUE(delegate.QuitOnCreate()); | |
1160 | |
1161 Window* window_in_wm = delegate.created_window(); | |
1162 ASSERT_TRUE(window_in_wm); | |
1163 | |
1164 // Change the bounds in the wm, and make sure the child sees it. | |
1165 window_in_wm->SetBounds(gfx::Rect(1, 11, 12, 101)); | |
1166 ASSERT_TRUE(WaitForBoundsToChange(window_in_second_client)); | |
1167 EXPECT_EQ(gfx::Rect(1, 11, 12, 101), window_in_second_client->bounds()); | |
1168 } | |
1169 | |
1170 } // namespace ws | |
1171 } // namespace mus | |
OLD | NEW |