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 "mojo/services/public/cpp/view_manager/view_manager.h" | |
6 | |
7 #include "base/auto_reset.h" | |
8 #include "base/bind.h" | |
9 #include "base/logging.h" | |
10 #include "mojo/application_manager/application_manager.h" | |
11 #include "mojo/public/cpp/application/application_connection.h" | |
12 #include "mojo/public/cpp/application/application_delegate.h" | |
13 #include "mojo/public/cpp/application/application_impl.h" | |
14 #include "mojo/public/cpp/application/service_provider_impl.h" | |
15 #include "mojo/public/interfaces/application/service_provider.mojom.h" | |
16 #include "mojo/services/public/cpp/geometry/geometry_util.h" | |
17 #include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h" | |
18 #include "mojo/services/public/cpp/view_manager/lib/view_private.h" | |
19 #include "mojo/services/public/cpp/view_manager/util.h" | |
20 #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" | |
21 #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" | |
22 #include "mojo/services/public/cpp/view_manager/view_observer.h" | |
23 #include "shell/shell_test_helper.h" | |
24 #include "testing/gtest/include/gtest/gtest.h" | |
25 | |
26 namespace mojo { | |
27 | |
28 namespace { | |
29 | |
30 const char kWindowManagerURL[] = "mojo:window_manager"; | |
31 const char kEmbeddedApp1URL[] = "mojo:embedded_app_1"; | |
32 | |
33 base::RunLoop* current_run_loop = NULL; | |
34 | |
35 void DoRunLoop() { | |
36 base::RunLoop run_loop; | |
37 current_run_loop = &run_loop; | |
38 current_run_loop->Run(); | |
39 current_run_loop = NULL; | |
40 } | |
41 | |
42 void QuitRunLoop() { | |
43 current_run_loop->Quit(); | |
44 } | |
45 | |
46 class ConnectApplicationLoader : public ApplicationLoader, | |
47 public ApplicationDelegate, | |
48 public ViewManagerDelegate { | |
49 public: | |
50 typedef base::Callback<void(ViewManager*, View*)> LoadedCallback; | |
51 | |
52 explicit ConnectApplicationLoader(const LoadedCallback& callback) | |
53 : callback_(callback) {} | |
54 ~ConnectApplicationLoader() override {} | |
55 | |
56 private: | |
57 // Overridden from ApplicationDelegate: | |
58 void Initialize(ApplicationImpl* app) override { | |
59 view_manager_client_factory_.reset( | |
60 new ViewManagerClientFactory(app->shell(), this)); | |
61 } | |
62 | |
63 // Overridden from ApplicationLoader: | |
64 void Load(ApplicationManager* manager, | |
65 const GURL& url, | |
66 ScopedMessagePipeHandle shell_handle, | |
67 LoadCallback callback) override { | |
68 ASSERT_TRUE(shell_handle.is_valid()); | |
69 scoped_ptr<ApplicationImpl> app( | |
70 new ApplicationImpl(this, shell_handle.Pass())); | |
71 apps_.push_back(app.release()); | |
72 } | |
73 | |
74 void OnApplicationError(ApplicationManager* manager, | |
75 const GURL& url) override {} | |
76 | |
77 bool ConfigureIncomingConnection(ApplicationConnection* connection) override { | |
78 connection->AddService(view_manager_client_factory_.get()); | |
79 return true; | |
80 } | |
81 | |
82 // Overridden from ViewManagerDelegate: | |
83 void OnEmbed(ViewManager* view_manager, | |
84 View* root, | |
85 ServiceProviderImpl* exported_services, | |
86 scoped_ptr<ServiceProvider> imported_services) override { | |
87 callback_.Run(view_manager, root); | |
88 } | |
89 void OnViewManagerDisconnected(ViewManager* view_manager) override {} | |
90 | |
91 ScopedVector<ApplicationImpl> apps_; | |
92 LoadedCallback callback_; | |
93 scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_; | |
94 | |
95 DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader); | |
96 }; | |
97 | |
98 class BoundsChangeObserver : public ViewObserver { | |
99 public: | |
100 explicit BoundsChangeObserver(View* view) : view_(view) {} | |
101 ~BoundsChangeObserver() override {} | |
102 | |
103 private: | |
104 // Overridden from ViewObserver: | |
105 void OnViewBoundsChanged(View* view, | |
106 const Rect& old_bounds, | |
107 const Rect& new_bounds) override { | |
108 DCHECK_EQ(view, view_); | |
109 QuitRunLoop(); | |
110 } | |
111 | |
112 View* view_; | |
113 | |
114 DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); | |
115 }; | |
116 | |
117 // Wait until the bounds of the supplied view change. | |
118 void WaitForBoundsToChange(View* view) { | |
119 BoundsChangeObserver observer(view); | |
120 view->AddObserver(&observer); | |
121 DoRunLoop(); | |
122 view->RemoveObserver(&observer); | |
123 } | |
124 | |
125 // Spins a runloop until the tree beginning at |root| has |tree_size| views | |
126 // (including |root|). | |
127 class TreeSizeMatchesObserver : public ViewObserver { | |
128 public: | |
129 TreeSizeMatchesObserver(View* tree, size_t tree_size) | |
130 : tree_(tree), | |
131 tree_size_(tree_size) {} | |
132 ~TreeSizeMatchesObserver() override {} | |
133 | |
134 bool IsTreeCorrectSize() { | |
135 return CountViews(tree_) == tree_size_; | |
136 } | |
137 | |
138 private: | |
139 // Overridden from ViewObserver: | |
140 void OnTreeChanged(const TreeChangeParams& params) override { | |
141 if (IsTreeCorrectSize()) | |
142 QuitRunLoop(); | |
143 } | |
144 | |
145 size_t CountViews(const View* view) const { | |
146 size_t count = 1; | |
147 View::Children::const_iterator it = view->children().begin(); | |
148 for (; it != view->children().end(); ++it) | |
149 count += CountViews(*it); | |
150 return count; | |
151 } | |
152 | |
153 View* tree_; | |
154 size_t tree_size_; | |
155 | |
156 DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver); | |
157 }; | |
158 | |
159 void WaitForTreeSizeToMatch(View* view, size_t tree_size) { | |
160 TreeSizeMatchesObserver observer(view, tree_size); | |
161 if (observer.IsTreeCorrectSize()) | |
162 return; | |
163 view->AddObserver(&observer); | |
164 DoRunLoop(); | |
165 view->RemoveObserver(&observer); | |
166 } | |
167 | |
168 // Utility class that waits for the destruction of some number of views and | |
169 // views. | |
170 class DestructionObserver : public ViewObserver { | |
171 public: | |
172 // |views| or |views| can be NULL. | |
173 explicit DestructionObserver(std::set<Id>* views) : views_(views) {} | |
174 | |
175 private: | |
176 // Overridden from ViewObserver: | |
177 void OnViewDestroyed(View* view) override { | |
178 std::set<Id>::iterator it = views_->find(view->id()); | |
179 if (it != views_->end()) | |
180 views_->erase(it); | |
181 if (CanQuit()) | |
182 QuitRunLoop(); | |
183 } | |
184 | |
185 bool CanQuit() { | |
186 return !views_ || views_->empty(); | |
187 } | |
188 | |
189 std::set<Id>* views_; | |
190 | |
191 DISALLOW_COPY_AND_ASSIGN(DestructionObserver); | |
192 }; | |
193 | |
194 void WaitForDestruction(ViewManager* view_manager, std::set<Id>* views) { | |
195 DestructionObserver observer(views); | |
196 DCHECK(views); | |
197 if (views) { | |
198 for (std::set<Id>::const_iterator it = views->begin(); | |
199 it != views->end(); ++it) { | |
200 view_manager->GetViewById(*it)->AddObserver(&observer); | |
201 } | |
202 } | |
203 DoRunLoop(); | |
204 } | |
205 | |
206 class OrderChangeObserver : public ViewObserver { | |
207 public: | |
208 OrderChangeObserver(View* view) : view_(view) { | |
209 view_->AddObserver(this); | |
210 } | |
211 ~OrderChangeObserver() override { view_->RemoveObserver(this); } | |
212 | |
213 private: | |
214 // Overridden from ViewObserver: | |
215 void OnViewReordered(View* view, | |
216 View* relative_view, | |
217 OrderDirection direction) override { | |
218 DCHECK_EQ(view, view_); | |
219 QuitRunLoop(); | |
220 } | |
221 | |
222 View* view_; | |
223 | |
224 DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); | |
225 }; | |
226 | |
227 void WaitForOrderChange(ViewManager* view_manager, View* view) { | |
228 OrderChangeObserver observer(view); | |
229 DoRunLoop(); | |
230 } | |
231 | |
232 // Tracks a view's destruction. Query is_valid() for current state. | |
233 class ViewTracker : public ViewObserver { | |
234 public: | |
235 explicit ViewTracker(View* view) : view_(view) { | |
236 view_->AddObserver(this); | |
237 } | |
238 ~ViewTracker() override { | |
239 if (view_) | |
240 view_->RemoveObserver(this); | |
241 } | |
242 | |
243 bool is_valid() const { return !!view_; } | |
244 | |
245 private: | |
246 // Overridden from ViewObserver: | |
247 void OnViewDestroyed(View* view) override { | |
248 DCHECK_EQ(view, view_); | |
249 view_ = NULL; | |
250 } | |
251 | |
252 int id_; | |
253 View* view_; | |
254 | |
255 DISALLOW_COPY_AND_ASSIGN(ViewTracker); | |
256 }; | |
257 | |
258 } // namespace | |
259 | |
260 // ViewManager ----------------------------------------------------------------- | |
261 | |
262 // These tests model synchronization of two peer connections to the view manager | |
263 // service, that are given access to some root view. | |
264 | |
265 class ViewManagerTest : public testing::Test { | |
266 public: | |
267 ViewManagerTest() | |
268 : connect_loop_(NULL), | |
269 loaded_view_manager_(NULL), | |
270 window_manager_(NULL), | |
271 commit_count_(0) {} | |
272 | |
273 protected: | |
274 ViewManager* window_manager() { return window_manager_; } | |
275 | |
276 View* CreateViewInParent(View* parent) { | |
277 ViewManager* parent_manager = ViewPrivate(parent).view_manager(); | |
278 View* view = View::Create(parent_manager); | |
279 view->SetVisible(true); | |
280 parent->AddChild(view); | |
281 return view; | |
282 } | |
283 | |
284 // Embeds another version of the test app @ view. | |
285 ViewManager* Embed(ViewManager* view_manager, View* view) { | |
286 DCHECK_EQ(view_manager, ViewPrivate(view).view_manager()); | |
287 view->Embed(kEmbeddedApp1URL); | |
288 RunRunLoop(); | |
289 return GetLoadedViewManager(); | |
290 } | |
291 | |
292 ViewManager* GetLoadedViewManager() { | |
293 ViewManager* view_manager = loaded_view_manager_; | |
294 loaded_view_manager_ = NULL; | |
295 return view_manager; | |
296 } | |
297 | |
298 void UnloadApplication(const GURL& url) { | |
299 test_helper_.SetLoaderForURL(scoped_ptr<ApplicationLoader>(), url); | |
300 } | |
301 | |
302 private: | |
303 // Overridden from testing::Test: | |
304 void SetUp() override { | |
305 ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind( | |
306 &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this)); | |
307 test_helper_.Init(); | |
308 test_helper_.SetLoaderForURL( | |
309 scoped_ptr<ApplicationLoader>( | |
310 new ConnectApplicationLoader(ready_callback)), | |
311 GURL(kWindowManagerURL)); | |
312 test_helper_.SetLoaderForURL( | |
313 scoped_ptr<ApplicationLoader>( | |
314 new ConnectApplicationLoader(ready_callback)), | |
315 GURL(kEmbeddedApp1URL)); | |
316 | |
317 // TODO(sky): resolve this. Need to establish initial connection. | |
318 } | |
319 | |
320 void OnViewManagerLoaded(ViewManager* view_manager, View* root) { | |
321 loaded_view_manager_ = view_manager; | |
322 connect_loop_->Quit(); | |
323 } | |
324 | |
325 void RunRunLoop() { | |
326 base::RunLoop run_loop; | |
327 connect_loop_ = &run_loop; | |
328 connect_loop_->Run(); | |
329 connect_loop_ = NULL; | |
330 } | |
331 | |
332 base::RunLoop* connect_loop_; | |
333 shell::ShellTestHelper test_helper_; | |
334 // Used to receive the most recent view manager loaded by an embed action. | |
335 ViewManager* loaded_view_manager_; | |
336 // The View Manager connection held by the window manager (app running at the | |
337 // root view). | |
338 ViewManager* window_manager_; | |
339 int commit_count_; | |
340 | |
341 DISALLOW_COPY_AND_ASSIGN(ViewManagerTest); | |
342 }; | |
343 | |
344 // TODO(sky): all of these tests are disabled as each test triggers running | |
345 // ViewsInit, which tries to register the same set of paths with the | |
346 // PathService, triggering a DCHECK. | |
347 TEST_F(ViewManagerTest, DISABLED_SetUp) {} | |
348 | |
349 TEST_F(ViewManagerTest, DISABLED_Embed) { | |
350 View* view = View::Create(window_manager()); | |
351 view->SetVisible(true); | |
352 window_manager()->GetRoots().front()->AddChild(view); | |
353 ViewManager* embedded = Embed(window_manager(), view); | |
354 EXPECT_TRUE(NULL != embedded); | |
355 | |
356 View* view_in_embedded = embedded->GetRoots().front(); | |
357 EXPECT_EQ(view->parent(), window_manager()->GetRoots().front()); | |
358 EXPECT_EQ(NULL, view_in_embedded->parent()); | |
359 } | |
360 | |
361 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see | |
362 // N11. | |
363 // TODO(sky): Update client lib to match server. | |
364 TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) { | |
365 View* view = View::Create(window_manager()); | |
366 view->SetVisible(true); | |
367 window_manager()->GetRoots().front()->AddChild(view); | |
368 View* nested = View::Create(window_manager()); | |
369 nested->SetVisible(true); | |
370 view->AddChild(nested); | |
371 | |
372 ViewManager* embedded = Embed(window_manager(), view); | |
373 EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(), | |
374 nested->id()); | |
375 EXPECT_TRUE(embedded->GetRoots().front()->children().empty()); | |
376 EXPECT_TRUE(nested->parent() == NULL); | |
377 } | |
378 | |
379 // http://crbug.com/396300 | |
380 TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) { | |
381 View* view = View::Create(window_manager()); | |
382 view->SetVisible(true); | |
383 window_manager()->GetRoots().front()->AddChild(view); | |
384 ViewManager* embedded = Embed(window_manager(), view); | |
385 | |
386 Id view_id = view->id(); | |
387 | |
388 UnloadApplication(GURL(kWindowManagerURL)); | |
389 | |
390 std::set<Id> views; | |
391 views.insert(view_id); | |
392 WaitForDestruction(embedded, &views); | |
393 | |
394 EXPECT_TRUE(embedded->GetRoots().empty()); | |
395 } | |
396 | |
397 // TODO(beng): write a replacement test for the one that once existed here: | |
398 // This test validates the following scenario: | |
399 // - a view originating from one connection | |
400 // - a view originating from a second connection | |
401 // + the connection originating the view is destroyed | |
402 // -> the view should still exist (since the second connection is live) but | |
403 // should be disconnected from any views. | |
404 // http://crbug.com/396300 | |
405 // | |
406 // TODO(beng): The new test should validate the scenario as described above | |
407 // except that the second connection still has a valid tree. | |
408 | |
409 // Verifies that bounds changes applied to a view hierarchy in one connection | |
410 // are reflected to another. | |
411 TEST_F(ViewManagerTest, DISABLED_SetBounds) { | |
412 View* view = View::Create(window_manager()); | |
413 view->SetVisible(true); | |
414 window_manager()->GetRoots().front()->AddChild(view); | |
415 ViewManager* embedded = Embed(window_manager(), view); | |
416 | |
417 View* view_in_embedded = embedded->GetViewById(view->id()); | |
418 EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); | |
419 | |
420 Rect rect; | |
421 rect.width = rect.height = 100; | |
422 view->SetBounds(rect); | |
423 EXPECT_NE(view->bounds(), view_in_embedded->bounds()); | |
424 WaitForBoundsToChange(view_in_embedded); | |
425 EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); | |
426 } | |
427 | |
428 // Verifies that bounds changes applied to a view owned by a different | |
429 // connection are refused. | |
430 TEST_F(ViewManagerTest, DISABLED_SetBoundsSecurity) { | |
431 View* view = View::Create(window_manager()); | |
432 view->SetVisible(true); | |
433 window_manager()->GetRoots().front()->AddChild(view); | |
434 ViewManager* embedded = Embed(window_manager(), view); | |
435 | |
436 View* view_in_embedded = embedded->GetViewById(view->id()); | |
437 Rect rect; | |
438 rect.width = 800; | |
439 rect.height = 600; | |
440 view->SetBounds(rect); | |
441 WaitForBoundsToChange(view_in_embedded); | |
442 | |
443 rect.width = 1024; | |
444 rect.height = 768; | |
445 view_in_embedded->SetBounds(rect); | |
446 // Bounds change should have been rejected. | |
447 EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); | |
448 } | |
449 | |
450 // Verifies that a view can only be destroyed by the connection that created it. | |
451 TEST_F(ViewManagerTest, DISABLED_DestroySecurity) { | |
452 View* view = View::Create(window_manager()); | |
453 view->SetVisible(true); | |
454 window_manager()->GetRoots().front()->AddChild(view); | |
455 ViewManager* embedded = Embed(window_manager(), view); | |
456 | |
457 View* view_in_embedded = embedded->GetViewById(view->id()); | |
458 | |
459 ViewTracker tracker2(view_in_embedded); | |
460 view_in_embedded->Destroy(); | |
461 // View should not have been destroyed. | |
462 EXPECT_TRUE(tracker2.is_valid()); | |
463 | |
464 ViewTracker tracker1(view); | |
465 view->Destroy(); | |
466 EXPECT_FALSE(tracker1.is_valid()); | |
467 } | |
468 | |
469 TEST_F(ViewManagerTest, DISABLED_MultiRoots) { | |
470 View* view1 = View::Create(window_manager()); | |
471 view1->SetVisible(true); | |
472 window_manager()->GetRoots().front()->AddChild(view1); | |
473 View* view2 = View::Create(window_manager()); | |
474 view2->SetVisible(true); | |
475 window_manager()->GetRoots().front()->AddChild(view2); | |
476 ViewManager* embedded1 = Embed(window_manager(), view1); | |
477 ViewManager* embedded2 = Embed(window_manager(), view2); | |
478 EXPECT_EQ(embedded1, embedded2); | |
479 } | |
480 | |
481 TEST_F(ViewManagerTest, DISABLED_EmbeddingIdentity) { | |
482 View* view = View::Create(window_manager()); | |
483 view->SetVisible(true); | |
484 window_manager()->GetRoots().front()->AddChild(view); | |
485 ViewManager* embedded = Embed(window_manager(), view); | |
486 EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL()); | |
487 } | |
488 | |
489 TEST_F(ViewManagerTest, DISABLED_Reorder) { | |
490 View* view1 = View::Create(window_manager()); | |
491 view1->SetVisible(true); | |
492 window_manager()->GetRoots().front()->AddChild(view1); | |
493 | |
494 ViewManager* embedded = Embed(window_manager(), view1); | |
495 | |
496 View* view11 = View::Create(embedded); | |
497 view11->SetVisible(true); | |
498 embedded->GetRoots().front()->AddChild(view11); | |
499 View* view12 = View::Create(embedded); | |
500 view12->SetVisible(true); | |
501 embedded->GetRoots().front()->AddChild(view12); | |
502 | |
503 View* view1_in_wm = window_manager()->GetViewById(view1->id()); | |
504 | |
505 { | |
506 WaitForTreeSizeToMatch(view1, 2u); | |
507 view11->MoveToFront(); | |
508 WaitForOrderChange(window_manager(), | |
509 window_manager()->GetViewById(view11->id())); | |
510 | |
511 EXPECT_EQ(view1_in_wm->children().front(), | |
512 window_manager()->GetViewById(view12->id())); | |
513 EXPECT_EQ(view1_in_wm->children().back(), | |
514 window_manager()->GetViewById(view11->id())); | |
515 } | |
516 | |
517 { | |
518 view11->MoveToBack(); | |
519 WaitForOrderChange(window_manager(), | |
520 window_manager()->GetViewById(view11->id())); | |
521 | |
522 EXPECT_EQ(view1_in_wm->children().front(), | |
523 window_manager()->GetViewById(view11->id())); | |
524 EXPECT_EQ(view1_in_wm->children().back(), | |
525 window_manager()->GetViewById(view12->id())); | |
526 } | |
527 } | |
528 | |
529 namespace { | |
530 | |
531 class VisibilityChangeObserver : public ViewObserver { | |
532 public: | |
533 explicit VisibilityChangeObserver(View* view) : view_(view) { | |
534 view_->AddObserver(this); | |
535 } | |
536 ~VisibilityChangeObserver() override { view_->RemoveObserver(this); } | |
537 | |
538 private: | |
539 // Overridden from ViewObserver: | |
540 void OnViewVisibilityChanged(View* view) override { | |
541 EXPECT_EQ(view, view_); | |
542 QuitRunLoop(); | |
543 } | |
544 | |
545 View* view_; | |
546 | |
547 DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver); | |
548 }; | |
549 | |
550 } // namespace | |
551 | |
552 TEST_F(ViewManagerTest, DISABLED_Visible) { | |
553 View* view1 = View::Create(window_manager()); | |
554 view1->SetVisible(true); | |
555 window_manager()->GetRoots().front()->AddChild(view1); | |
556 | |
557 // Embed another app and verify initial state. | |
558 ViewManager* embedded = Embed(window_manager(), view1); | |
559 ASSERT_EQ(1u, embedded->GetRoots().size()); | |
560 View* embedded_root = embedded->GetRoots().front(); | |
561 EXPECT_TRUE(embedded_root->visible()); | |
562 EXPECT_TRUE(embedded_root->IsDrawn()); | |
563 | |
564 // Change the visible state from the first connection and verify its mirrored | |
565 // correctly to the embedded app. | |
566 { | |
567 VisibilityChangeObserver observer(embedded_root); | |
568 view1->SetVisible(false); | |
569 DoRunLoop(); | |
570 } | |
571 | |
572 EXPECT_FALSE(view1->visible()); | |
573 EXPECT_FALSE(view1->IsDrawn()); | |
574 | |
575 EXPECT_FALSE(embedded_root->visible()); | |
576 EXPECT_FALSE(embedded_root->IsDrawn()); | |
577 | |
578 // Make the node visible again. | |
579 { | |
580 VisibilityChangeObserver observer(embedded_root); | |
581 view1->SetVisible(true); | |
582 DoRunLoop(); | |
583 } | |
584 | |
585 EXPECT_TRUE(view1->visible()); | |
586 EXPECT_TRUE(view1->IsDrawn()); | |
587 | |
588 EXPECT_TRUE(embedded_root->visible()); | |
589 EXPECT_TRUE(embedded_root->IsDrawn()); | |
590 } | |
591 | |
592 namespace { | |
593 | |
594 class DrawnChangeObserver : public ViewObserver { | |
595 public: | |
596 explicit DrawnChangeObserver(View* view) : view_(view) { | |
597 view_->AddObserver(this); | |
598 } | |
599 ~DrawnChangeObserver() override { view_->RemoveObserver(this); } | |
600 | |
601 private: | |
602 // Overridden from ViewObserver: | |
603 void OnViewDrawnChanged(View* view) override { | |
604 EXPECT_EQ(view, view_); | |
605 QuitRunLoop(); | |
606 } | |
607 | |
608 View* view_; | |
609 | |
610 DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver); | |
611 }; | |
612 | |
613 } // namespace | |
614 | |
615 TEST_F(ViewManagerTest, DISABLED_Drawn) { | |
616 View* view1 = View::Create(window_manager()); | |
617 view1->SetVisible(true); | |
618 window_manager()->GetRoots().front()->AddChild(view1); | |
619 | |
620 // Embed another app and verify initial state. | |
621 ViewManager* embedded = Embed(window_manager(), view1); | |
622 ASSERT_EQ(1u, embedded->GetRoots().size()); | |
623 View* embedded_root = embedded->GetRoots().front(); | |
624 EXPECT_TRUE(embedded_root->visible()); | |
625 EXPECT_TRUE(embedded_root->IsDrawn()); | |
626 | |
627 // Change the visibility of the root, this should propagate a drawn state | |
628 // change to |embedded|. | |
629 { | |
630 DrawnChangeObserver observer(embedded_root); | |
631 window_manager()->GetRoots().front()->SetVisible(false); | |
632 DoRunLoop(); | |
633 } | |
634 | |
635 EXPECT_TRUE(view1->visible()); | |
636 EXPECT_FALSE(view1->IsDrawn()); | |
637 | |
638 EXPECT_TRUE(embedded_root->visible()); | |
639 EXPECT_FALSE(embedded_root->IsDrawn()); | |
640 } | |
641 | |
642 // TODO(beng): tests for view event dispatcher. | |
643 // - verify that we see events for all views. | |
644 | |
645 // TODO(beng): tests for focus: | |
646 // - focus between two views known to a connection | |
647 // - focus between views unknown to one of the connections. | |
648 // - focus between views unknown to either connection. | |
649 | |
650 // TODO(sky): need test of root being destroyed with existing views. See | |
651 // 434555 for specific case. | |
652 | |
653 } // namespace mojo | |
OLD | NEW |