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.
6 #include "base/logging.h"
7 #include "base/run_loop.h"
8 #include "components/mus/public/cpp/tests/view_manager_test_base.h"
9 #include "components/mus/public/cpp/util.h"
10 #include "components/mus/public/cpp/view_observer.h"
11 #include "components/mus/public/cpp/view_tree_connection.h"
12 #include "components/mus/public/cpp/view_tree_delegate.h"
13 #include "mojo/application/public/cpp/application_connection.h"
14 #include "mojo/application/public/cpp/application_impl.h"
15 #include "mojo/application/public/cpp/application_test_base.h"
16 #include "ui/mojo/geometry/geometry_util.h"
22 class BoundsChangeObserver
: public ViewObserver
{
24 explicit BoundsChangeObserver(View
* view
) : view_(view
) {
25 view_
->AddObserver(this);
27 ~BoundsChangeObserver() override
{ view_
->RemoveObserver(this); }
30 // Overridden from ViewObserver:
31 void OnViewBoundsChanged(View
* view
,
32 const mojo::Rect
& old_bounds
,
33 const mojo::Rect
& new_bounds
) override
{
34 DCHECK_EQ(view
, view_
);
35 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
40 MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver
);
43 // Wait until the bounds of the supplied view change; returns false on timeout.
44 bool WaitForBoundsToChange(View
* view
) {
45 BoundsChangeObserver
observer(view
);
46 return ViewManagerTestBase::DoRunLoopWithTimeout();
49 // Spins a run loop until the tree beginning at |root| has |tree_size| views
50 // (including |root|).
51 class TreeSizeMatchesObserver
: public ViewObserver
{
53 TreeSizeMatchesObserver(View
* tree
, size_t tree_size
)
54 : tree_(tree
), tree_size_(tree_size
) {
55 tree_
->AddObserver(this);
57 ~TreeSizeMatchesObserver() override
{ tree_
->RemoveObserver(this); }
59 bool IsTreeCorrectSize() { return CountViews(tree_
) == tree_size_
; }
62 // Overridden from ViewObserver:
63 void OnTreeChanged(const TreeChangeParams
& params
) override
{
64 if (IsTreeCorrectSize())
65 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
68 size_t CountViews(const View
* view
) const {
70 View::Children::const_iterator it
= view
->children().begin();
71 for (; it
!= view
->children().end(); ++it
)
72 count
+= CountViews(*it
);
79 MOJO_DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver
);
82 // Wait until |view| has |tree_size| descendants; returns false on timeout. The
83 // count includes |view|. For example, if you want to wait for |view| to have
84 // a single child, use a |tree_size| of 2.
85 bool WaitForTreeSizeToMatch(View
* view
, size_t tree_size
) {
86 TreeSizeMatchesObserver
observer(view
, tree_size
);
87 return observer
.IsTreeCorrectSize() ||
88 ViewManagerTestBase::DoRunLoopWithTimeout();
91 class OrderChangeObserver
: public ViewObserver
{
93 OrderChangeObserver(View
* view
) : view_(view
) { view_
->AddObserver(this); }
94 ~OrderChangeObserver() override
{ view_
->RemoveObserver(this); }
97 // Overridden from ViewObserver:
98 void OnViewReordered(View
* view
,
100 mojo::OrderDirection direction
) override
{
101 DCHECK_EQ(view
, view_
);
102 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
107 MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver
);
110 // Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
111 bool WaitForOrderChange(ViewTreeConnection
* connection
, View
* view
) {
112 OrderChangeObserver
observer(view
);
113 return ViewManagerTestBase::DoRunLoopWithTimeout();
116 // Tracks a view's destruction. Query is_valid() for current state.
117 class ViewTracker
: public ViewObserver
{
119 explicit ViewTracker(View
* view
) : view_(view
) { view_
->AddObserver(this); }
120 ~ViewTracker() override
{
122 view_
->RemoveObserver(this);
125 bool is_valid() const { return !!view_
; }
128 // Overridden from ViewObserver:
129 void OnViewDestroyed(View
* view
) override
{
130 DCHECK_EQ(view
, view_
);
136 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTracker
);
141 // ViewManager -----------------------------------------------------------------
144 EmbedResult(ViewTreeConnection
* connection
, ConnectionSpecificId id
)
145 : connection(connection
), connection_id(id
) {}
146 EmbedResult() : connection(nullptr), connection_id(0) {}
148 ViewTreeConnection
* connection
;
150 // The id supplied to the callback from OnEmbed(). Depending upon the
151 // access policy this may or may not match the connection id of
153 ConnectionSpecificId connection_id
;
156 // These tests model synchronization of two peer connections to the view manager
157 // service, that are given access to some root view.
159 class ViewManagerTest
: public ViewManagerTestBase
{
163 // Embeds another version of the test app @ view. This runs a run loop until
164 // a response is received, or a timeout. On success the new ViewManager is
166 EmbedResult
Embed(View
* view
) {
167 return Embed(view
, mojo::ViewTree::ACCESS_POLICY_DEFAULT
);
170 EmbedResult
Embed(View
* view
, uint32_t access_policy_bitmask
) {
171 DCHECK(!embed_details_
);
172 embed_details_
.reset(new EmbedDetails
);
173 view
->Embed(ConnectToApplicationAndGetViewManagerClient(),
174 access_policy_bitmask
,
175 base::Bind(&ViewManagerTest::EmbedCallbackImpl
,
176 base::Unretained(this)));
177 embed_details_
->waiting
= true;
178 if (!ViewManagerTestBase::DoRunLoopWithTimeout())
179 return EmbedResult();
180 const EmbedResult
result(embed_details_
->connection
,
181 embed_details_
->connection_id
);
182 embed_details_
.reset();
186 // Establishes a connection to this application and asks for a
188 mojo::ViewTreeClientPtr
ConnectToApplicationAndGetViewManagerClient() {
189 mojo::URLRequestPtr
request(mojo::URLRequest::New());
190 request
->url
= mojo::String::From(application_impl()->url());
191 scoped_ptr
<mojo::ApplicationConnection
> connection
=
192 application_impl()->ConnectToApplication(request
.Pass());
193 mojo::ViewTreeClientPtr client
;
194 connection
->ConnectToService(&client
);
195 return client
.Pass();
198 // ViewManagerTestBase:
199 void OnEmbed(View
* root
) override
{
200 if (!embed_details_
) {
201 ViewManagerTestBase::OnEmbed(root
);
205 embed_details_
->connection
= root
->connection();
206 if (embed_details_
->callback_run
)
207 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
211 // Used to track the state of a call to view->Embed().
212 struct EmbedDetails
{
214 : callback_run(false),
218 connection(nullptr) {}
220 // The callback supplied to Embed() was received.
223 // The boolean supplied to the Embed() callback.
226 // Whether a MessageLoop is running.
229 // Connection id supplied to the Embed() callback.
230 ConnectionSpecificId connection_id
;
232 // The ViewTreeConnection that resulted from the Embed(). null if |result|
234 ViewTreeConnection
* connection
;
237 void EmbedCallbackImpl(bool result
, ConnectionSpecificId connection_id
) {
238 embed_details_
->callback_run
= true;
239 embed_details_
->result
= result
;
240 embed_details_
->connection_id
= connection_id
;
241 if (embed_details_
->waiting
&& (!result
|| embed_details_
->connection
))
242 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
245 scoped_ptr
<EmbedDetails
> embed_details_
;
247 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTest
);
250 TEST_F(ViewManagerTest
, RootView
) {
251 ASSERT_NE(nullptr, window_manager());
252 EXPECT_NE(nullptr, window_manager()->GetRoot());
255 TEST_F(ViewManagerTest
, Embed
) {
256 View
* view
= window_manager()->CreateView();
257 ASSERT_NE(nullptr, view
);
258 view
->SetVisible(true);
259 window_manager()->GetRoot()->AddChild(view
);
260 ViewTreeConnection
* embedded
= Embed(view
).connection
;
261 ASSERT_NE(nullptr, embedded
);
263 View
* view_in_embedded
= embedded
->GetRoot();
264 ASSERT_NE(nullptr, view_in_embedded
);
265 EXPECT_EQ(view
->id(), view_in_embedded
->id());
266 EXPECT_EQ(nullptr, view_in_embedded
->parent());
267 EXPECT_TRUE(view_in_embedded
->children().empty());
270 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see
272 TEST_F(ViewManagerTest
, EmbeddedDoesntSeeChild
) {
273 View
* view
= window_manager()->CreateView();
274 ASSERT_NE(nullptr, view
);
275 view
->SetVisible(true);
276 window_manager()->GetRoot()->AddChild(view
);
277 View
* nested
= window_manager()->CreateView();
278 ASSERT_NE(nullptr, nested
);
279 nested
->SetVisible(true);
280 view
->AddChild(nested
);
282 ViewTreeConnection
* embedded
= Embed(view
).connection
;
283 ASSERT_NE(nullptr, embedded
);
284 View
* view_in_embedded
= embedded
->GetRoot();
285 EXPECT_EQ(view
->id(), view_in_embedded
->id());
286 EXPECT_EQ(nullptr, view_in_embedded
->parent());
287 EXPECT_TRUE(view_in_embedded
->children().empty());
290 // TODO(beng): write a replacement test for the one that once existed here:
291 // This test validates the following scenario:
292 // - a view originating from one connection
293 // - a view originating from a second connection
294 // + the connection originating the view is destroyed
295 // -> the view should still exist (since the second connection is live) but
296 // should be disconnected from any views.
297 // http://crbug.com/396300
299 // TODO(beng): The new test should validate the scenario as described above
300 // except that the second connection still has a valid tree.
302 // Verifies that bounds changes applied to a view hierarchy in one connection
303 // are reflected to another.
304 TEST_F(ViewManagerTest
, SetBounds
) {
305 View
* view
= window_manager()->CreateView();
306 view
->SetVisible(true);
307 window_manager()->GetRoot()->AddChild(view
);
308 ViewTreeConnection
* embedded
= Embed(view
).connection
;
309 ASSERT_NE(nullptr, embedded
);
311 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
312 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
315 rect
.width
= rect
.height
= 100;
316 view
->SetBounds(rect
);
317 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded
));
318 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
321 // Verifies that bounds changes applied to a view owned by a different
322 // connection are refused.
323 TEST_F(ViewManagerTest
, SetBoundsSecurity
) {
324 View
* view
= window_manager()->CreateView();
325 view
->SetVisible(true);
326 window_manager()->GetRoot()->AddChild(view
);
327 ViewTreeConnection
* embedded
= Embed(view
).connection
;
328 ASSERT_NE(nullptr, embedded
);
330 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
334 view
->SetBounds(rect
);
335 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded
));
339 view_in_embedded
->SetBounds(rect
);
340 // Bounds change should have been rejected.
341 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
344 // Verifies that a view can only be destroyed by the connection that created it.
345 TEST_F(ViewManagerTest
, DestroySecurity
) {
346 View
* view
= window_manager()->CreateView();
347 view
->SetVisible(true);
348 window_manager()->GetRoot()->AddChild(view
);
349 ViewTreeConnection
* embedded
= Embed(view
).connection
;
350 ASSERT_NE(nullptr, embedded
);
352 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
354 ViewTracker
tracker2(view_in_embedded
);
355 view_in_embedded
->Destroy();
356 // View should not have been destroyed.
357 EXPECT_TRUE(tracker2
.is_valid());
359 ViewTracker
tracker1(view
);
361 EXPECT_FALSE(tracker1
.is_valid());
364 TEST_F(ViewManagerTest
, MultiRoots
) {
365 View
* view1
= window_manager()->CreateView();
366 view1
->SetVisible(true);
367 window_manager()->GetRoot()->AddChild(view1
);
368 View
* view2
= window_manager()->CreateView();
369 view2
->SetVisible(true);
370 window_manager()->GetRoot()->AddChild(view2
);
371 ViewTreeConnection
* embedded1
= Embed(view1
).connection
;
372 ASSERT_NE(nullptr, embedded1
);
373 ViewTreeConnection
* embedded2
= Embed(view2
).connection
;
374 ASSERT_NE(nullptr, embedded2
);
375 EXPECT_NE(embedded1
, embedded2
);
378 // TODO(alhaad): Currently, the RunLoop gets stuck waiting for order change.
379 // Debug and re-enable this.
380 TEST_F(ViewManagerTest
, DISABLED_Reorder
) {
381 View
* view1
= window_manager()->CreateView();
382 view1
->SetVisible(true);
383 window_manager()->GetRoot()->AddChild(view1
);
385 ViewTreeConnection
* embedded
= Embed(view1
).connection
;
386 ASSERT_NE(nullptr, embedded
);
388 View
* view11
= embedded
->CreateView();
389 view11
->SetVisible(true);
390 embedded
->GetRoot()->AddChild(view11
);
391 View
* view12
= embedded
->CreateView();
392 view12
->SetVisible(true);
393 embedded
->GetRoot()->AddChild(view12
);
395 View
* root_in_embedded
= embedded
->GetRoot();
398 ASSERT_TRUE(WaitForTreeSizeToMatch(root_in_embedded
, 3u));
399 view11
->MoveToFront();
400 ASSERT_TRUE(WaitForOrderChange(embedded
, root_in_embedded
));
402 EXPECT_EQ(root_in_embedded
->children().front(),
403 embedded
->GetViewById(view12
->id()));
404 EXPECT_EQ(root_in_embedded
->children().back(),
405 embedded
->GetViewById(view11
->id()));
409 view11
->MoveToBack();
411 WaitForOrderChange(embedded
, embedded
->GetViewById(view11
->id())));
413 EXPECT_EQ(root_in_embedded
->children().front(),
414 embedded
->GetViewById(view11
->id()));
415 EXPECT_EQ(root_in_embedded
->children().back(),
416 embedded
->GetViewById(view12
->id()));
422 class VisibilityChangeObserver
: public ViewObserver
{
424 explicit VisibilityChangeObserver(View
* view
) : view_(view
) {
425 view_
->AddObserver(this);
427 ~VisibilityChangeObserver() override
{ view_
->RemoveObserver(this); }
430 // Overridden from ViewObserver:
431 void OnViewVisibilityChanged(View
* view
) override
{
432 EXPECT_EQ(view
, view_
);
433 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
438 MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver
);
443 TEST_F(ViewManagerTest
, Visible
) {
444 View
* view1
= window_manager()->CreateView();
445 view1
->SetVisible(true);
446 window_manager()->GetRoot()->AddChild(view1
);
448 // Embed another app and verify initial state.
449 ViewTreeConnection
* embedded
= Embed(view1
).connection
;
450 ASSERT_NE(nullptr, embedded
);
451 ASSERT_NE(nullptr, embedded
->GetRoot());
452 View
* embedded_root
= embedded
->GetRoot();
453 EXPECT_TRUE(embedded_root
->visible());
454 EXPECT_TRUE(embedded_root
->IsDrawn());
456 // Change the visible state from the first connection and verify its mirrored
457 // correctly to the embedded app.
459 VisibilityChangeObserver
observer(embedded_root
);
460 view1
->SetVisible(false);
461 ASSERT_TRUE(ViewManagerTestBase::DoRunLoopWithTimeout());
464 EXPECT_FALSE(view1
->visible());
465 EXPECT_FALSE(view1
->IsDrawn());
467 EXPECT_FALSE(embedded_root
->visible());
468 EXPECT_FALSE(embedded_root
->IsDrawn());
470 // Make the node visible again.
472 VisibilityChangeObserver
observer(embedded_root
);
473 view1
->SetVisible(true);
474 ASSERT_TRUE(ViewManagerTestBase::DoRunLoopWithTimeout());
477 EXPECT_TRUE(view1
->visible());
478 EXPECT_TRUE(view1
->IsDrawn());
480 EXPECT_TRUE(embedded_root
->visible());
481 EXPECT_TRUE(embedded_root
->IsDrawn());
486 class DrawnChangeObserver
: public ViewObserver
{
488 explicit DrawnChangeObserver(View
* view
) : view_(view
) {
489 view_
->AddObserver(this);
491 ~DrawnChangeObserver() override
{ view_
->RemoveObserver(this); }
494 // Overridden from ViewObserver:
495 void OnViewDrawnChanged(View
* view
) override
{
496 EXPECT_EQ(view
, view_
);
497 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
502 MOJO_DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver
);
507 TEST_F(ViewManagerTest
, Drawn
) {
508 View
* view1
= window_manager()->CreateView();
509 view1
->SetVisible(true);
510 window_manager()->GetRoot()->AddChild(view1
);
512 // Embed another app and verify initial state.
513 ViewTreeConnection
* embedded
= Embed(view1
).connection
;
514 ASSERT_NE(nullptr, embedded
);
515 ASSERT_NE(nullptr, embedded
->GetRoot());
516 View
* embedded_root
= embedded
->GetRoot();
517 EXPECT_TRUE(embedded_root
->visible());
518 EXPECT_TRUE(embedded_root
->IsDrawn());
520 // Change the visibility of the root, this should propagate a drawn state
521 // change to |embedded|.
523 DrawnChangeObserver
observer(embedded_root
);
524 window_manager()->GetRoot()->SetVisible(false);
525 ASSERT_TRUE(DoRunLoopWithTimeout());
528 EXPECT_TRUE(view1
->visible());
529 EXPECT_FALSE(view1
->IsDrawn());
531 EXPECT_TRUE(embedded_root
->visible());
532 EXPECT_FALSE(embedded_root
->IsDrawn());
535 // TODO(beng): tests for view event dispatcher.
536 // - verify that we see events for all views.
540 class FocusChangeObserver
: public ViewObserver
{
542 explicit FocusChangeObserver(View
* view
)
543 : view_(view
), last_gained_focus_(nullptr), last_lost_focus_(nullptr) {
544 view_
->AddObserver(this);
546 ~FocusChangeObserver() override
{ view_
->RemoveObserver(this); }
548 View
* last_gained_focus() { return last_gained_focus_
; }
550 View
* last_lost_focus() { return last_lost_focus_
; }
553 // Overridden from ViewObserver.
554 void OnViewFocusChanged(View
* gained_focus
, View
* lost_focus
) override
{
555 EXPECT_TRUE(!gained_focus
|| gained_focus
->HasFocus());
556 EXPECT_FALSE(lost_focus
&& lost_focus
->HasFocus());
557 last_gained_focus_
= gained_focus
;
558 last_lost_focus_
= lost_focus
;
559 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
563 View
* last_gained_focus_
;
564 View
* last_lost_focus_
;
566 MOJO_DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver
);
571 TEST_F(ViewManagerTest
, Focus
) {
572 View
* view1
= window_manager()->CreateView();
573 view1
->SetVisible(true);
574 window_manager()->GetRoot()->AddChild(view1
);
576 ViewTreeConnection
* embedded
= Embed(view1
).connection
;
577 ASSERT_NE(nullptr, embedded
);
578 View
* view11
= embedded
->CreateView();
579 view11
->SetVisible(true);
580 embedded
->GetRoot()->AddChild(view11
);
582 // TODO(alhaad): Figure out why switching focus between views from different
583 // connections is causing the tests to crash and add tests for that.
585 View
* embedded_root
= embedded
->GetRoot();
586 FocusChangeObserver
observer(embedded_root
);
587 embedded_root
->SetFocus();
588 ASSERT_TRUE(DoRunLoopWithTimeout());
589 ASSERT_NE(nullptr, observer
.last_gained_focus());
590 EXPECT_EQ(embedded_root
->id(), observer
.last_gained_focus()->id());
593 FocusChangeObserver
observer(view11
);
595 ASSERT_TRUE(DoRunLoopWithTimeout());
596 ASSERT_NE(nullptr, observer
.last_gained_focus());
597 ASSERT_NE(nullptr, observer
.last_lost_focus());
598 EXPECT_EQ(view11
->id(), observer
.last_gained_focus()->id());
599 EXPECT_EQ(embedded
->GetRoot()->id(), observer
.last_lost_focus()->id());
602 // Add an observer on the View that loses focus, and make sure the observer
603 // sees the right values.
604 FocusChangeObserver
observer(view11
);
605 embedded
->GetRoot()->SetFocus();
606 ASSERT_TRUE(DoRunLoopWithTimeout());
607 ASSERT_NE(nullptr, observer
.last_gained_focus());
608 ASSERT_NE(nullptr, observer
.last_lost_focus());
609 EXPECT_EQ(view11
->id(), observer
.last_lost_focus()->id());
610 EXPECT_EQ(embedded
->GetRoot()->id(), observer
.last_gained_focus()->id());
616 class DestroyedChangedObserver
: public ViewObserver
{
618 DestroyedChangedObserver(ViewManagerTestBase
* test
,
621 : test_(test
), view_(view
), got_destroy_(got_destroy
) {
622 view_
->AddObserver(this);
624 ~DestroyedChangedObserver() override
{
626 view_
->RemoveObserver(this);
630 // Overridden from ViewObserver:
631 void OnViewDestroyed(View
* view
) override
{
632 EXPECT_EQ(view
, view_
);
633 view_
->RemoveObserver(this);
634 *got_destroy_
= true;
637 // We should always get OnViewDestroyed() before OnConnectionLost().
638 EXPECT_FALSE(test_
->view_tree_connection_destroyed());
641 ViewManagerTestBase
* test_
;
645 MOJO_DISALLOW_COPY_AND_ASSIGN(DestroyedChangedObserver
);
650 // Verifies deleting a ViewManager sends the right notifications.
651 TEST_F(ViewManagerTest
, DeleteViewManager
) {
652 View
* view
= window_manager()->CreateView();
653 ASSERT_NE(nullptr, view
);
654 view
->SetVisible(true);
655 window_manager()->GetRoot()->AddChild(view
);
656 ViewTreeConnection
* connection
= Embed(view
).connection
;
657 ASSERT_TRUE(connection
);
658 bool got_destroy
= false;
659 DestroyedChangedObserver
observer(this, connection
->GetRoot(), &got_destroy
);
661 EXPECT_TRUE(view_tree_connection_destroyed());
662 EXPECT_TRUE(got_destroy
);
665 // Verifies two Embed()s in the same view trigger deletion of the first
667 TEST_F(ViewManagerTest
, DisconnectTriggersDelete
) {
668 View
* view
= window_manager()->CreateView();
669 ASSERT_NE(nullptr, view
);
670 view
->SetVisible(true);
671 window_manager()->GetRoot()->AddChild(view
);
672 ViewTreeConnection
* connection
= Embed(view
).connection
;
673 EXPECT_NE(connection
, window_manager());
674 View
* embedded_view
= connection
->CreateView();
675 // Embed again, this should trigger disconnect and deletion of connection.
677 DestroyedChangedObserver
observer(this, embedded_view
, &got_destroy
);
678 EXPECT_FALSE(view_tree_connection_destroyed());
680 EXPECT_TRUE(view_tree_connection_destroyed());
683 class ViewRemovedFromParentObserver
: public ViewObserver
{
685 explicit ViewRemovedFromParentObserver(View
* view
)
686 : view_(view
), was_removed_(false) {
687 view_
->AddObserver(this);
689 ~ViewRemovedFromParentObserver() override
{ view_
->RemoveObserver(this); }
691 bool was_removed() const { return was_removed_
; }
694 // Overridden from ViewObserver:
695 void OnTreeChanged(const TreeChangeParams
& params
) override
{
696 if (params
.target
== view_
&& !params
.new_parent
)
703 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewRemovedFromParentObserver
);
706 TEST_F(ViewManagerTest
, EmbedRemovesChildren
) {
707 View
* view1
= window_manager()->CreateView();
708 View
* view2
= window_manager()->CreateView();
709 window_manager()->GetRoot()->AddChild(view1
);
710 view1
->AddChild(view2
);
712 ViewRemovedFromParentObserver
observer(view2
);
713 view1
->Embed(ConnectToApplicationAndGetViewManagerClient());
714 EXPECT_TRUE(observer
.was_removed());
715 EXPECT_EQ(nullptr, view2
->parent());
716 EXPECT_TRUE(view1
->children().empty());
718 // Run the message loop so the Embed() call above completes. Without this
719 // we may end up reconnecting to the test and rerunning the test, which is
720 // problematic since the other services don't shut down.
721 ASSERT_TRUE(DoRunLoopWithTimeout());
726 class DestroyObserver
: public ViewObserver
{
728 DestroyObserver(ViewManagerTestBase
* test
,
729 ViewTreeConnection
* connection
,
731 : test_(test
), got_destroy_(got_destroy
) {
732 connection
->GetRoot()->AddObserver(this);
734 ~DestroyObserver() override
{}
737 // Overridden from ViewObserver:
738 void OnViewDestroyed(View
* view
) override
{
739 *got_destroy_
= true;
740 view
->RemoveObserver(this);
742 // We should always get OnViewDestroyed() before OnViewManagerDestroyed().
743 EXPECT_FALSE(test_
->view_tree_connection_destroyed());
745 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
748 ViewManagerTestBase
* test_
;
751 MOJO_DISALLOW_COPY_AND_ASSIGN(DestroyObserver
);
756 // Verifies deleting a View that is the root of another connection notifies
757 // observers in the right order (OnViewDestroyed() before
758 // OnViewManagerDestroyed()).
759 TEST_F(ViewManagerTest
, ViewManagerDestroyedAfterRootObserver
) {
760 View
* embed_view
= window_manager()->CreateView();
761 window_manager()->GetRoot()->AddChild(embed_view
);
763 ViewTreeConnection
* embedded_connection
= Embed(embed_view
).connection
;
765 bool got_destroy
= false;
766 DestroyObserver
observer(this, embedded_connection
, &got_destroy
);
767 // Delete the view |embedded_connection| is embedded in. This is async,
768 // but will eventually trigger deleting |embedded_connection|.
769 embed_view
->Destroy();
770 EXPECT_TRUE(DoRunLoopWithTimeout());
771 EXPECT_TRUE(got_destroy
);
774 // Verifies an embed root sees views created beneath it from another
776 TEST_F(ViewManagerTest
, EmbedRootSeesHierarchyChanged
) {
777 View
* embed_view
= window_manager()->CreateView();
778 window_manager()->GetRoot()->AddChild(embed_view
);
780 ViewTreeConnection
* vm2
=
781 Embed(embed_view
, mojo::ViewTree::ACCESS_POLICY_EMBED_ROOT
).connection
;
782 View
* vm2_v1
= vm2
->CreateView();
783 vm2
->GetRoot()->AddChild(vm2_v1
);
785 ViewTreeConnection
* vm3
= Embed(vm2_v1
).connection
;
786 View
* vm3_v1
= vm3
->CreateView();
787 vm3
->GetRoot()->AddChild(vm3_v1
);
789 // As |vm2| is an embed root it should get notified about |vm3_v1|.
790 ASSERT_TRUE(WaitForTreeSizeToMatch(vm2_v1
, 2));
793 TEST_F(ViewManagerTest
, EmbedFromEmbedRoot
) {
794 View
* embed_view
= window_manager()->CreateView();
795 window_manager()->GetRoot()->AddChild(embed_view
);
797 // Give the connection embedded at |embed_view| embed root powers.
798 const EmbedResult result1
=
799 Embed(embed_view
, mojo::ViewTree::ACCESS_POLICY_EMBED_ROOT
);
800 ViewTreeConnection
* vm2
= result1
.connection
;
801 EXPECT_EQ(result1
.connection_id
, vm2
->GetConnectionId());
802 View
* vm2_v1
= vm2
->CreateView();
803 vm2
->GetRoot()->AddChild(vm2_v1
);
805 const EmbedResult result2
= Embed(vm2_v1
);
806 ViewTreeConnection
* vm3
= result2
.connection
;
807 EXPECT_EQ(result2
.connection_id
, vm3
->GetConnectionId());
808 View
* vm3_v1
= vm3
->CreateView();
809 vm3
->GetRoot()->AddChild(vm3_v1
);
811 // Embed from v3, the callback should not get the connection id as vm3 is not
813 const EmbedResult result3
= Embed(vm3_v1
);
814 ASSERT_TRUE(result3
.connection
);
815 EXPECT_EQ(0, result3
.connection_id
);
817 // As |vm2| is an embed root it should get notified about |vm3_v1|.
818 ASSERT_TRUE(WaitForTreeSizeToMatch(vm2_v1
, 2));
820 // Embed() from vm2 in vm3_v1. This is allowed as vm2 is an embed root, and
821 // further the callback should see the connection id.
822 ASSERT_EQ(1u, vm2_v1
->children().size());
823 View
* vm3_v1_in_vm2
= vm2_v1
->children()[0];
824 const EmbedResult result4
= Embed(vm3_v1_in_vm2
);
825 ASSERT_TRUE(result4
.connection
);
826 EXPECT_EQ(result4
.connection_id
, result4
.connection
->GetConnectionId());