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/view_manager/public/cpp/tests/view_manager_test_base.h"
9 #include "components/view_manager/public/cpp/util.h"
10 #include "components/view_manager/public/cpp/view_observer.h"
11 #include "components/view_manager/public/cpp/view_tree_connection.h"
12 #include "components/view_manager/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 Rect
& old_bounds
,
33 const 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 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 DCHECK(!embed_details_
);
168 embed_details_
.reset(new EmbedDetails
);
169 view
->Embed(ConnectToApplicationAndGetViewManagerClient(),
170 base::Bind(&ViewManagerTest::EmbedCallbackImpl
,
171 base::Unretained(this)));
172 embed_details_
->waiting
= true;
173 if (!ViewManagerTestBase::DoRunLoopWithTimeout())
174 return EmbedResult();
175 const EmbedResult
result(embed_details_
->connection
,
176 embed_details_
->connection_id
);
177 embed_details_
.reset();
181 // Establishes a connection to this application and asks for a
183 mojo::ViewTreeClientPtr
ConnectToApplicationAndGetViewManagerClient() {
184 mojo::URLRequestPtr
request(mojo::URLRequest::New());
185 request
->url
= mojo::String::From(application_impl()->url());
186 scoped_ptr
<ApplicationConnection
> connection
=
187 application_impl()->ConnectToApplication(request
.Pass());
188 mojo::ViewTreeClientPtr client
;
189 connection
->ConnectToService(&client
);
190 return client
.Pass();
193 // ViewManagerTestBase:
194 void OnEmbed(View
* root
) override
{
195 if (!embed_details_
) {
196 ViewManagerTestBase::OnEmbed(root
);
200 embed_details_
->connection
= root
->connection();
201 if (embed_details_
->callback_run
)
202 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
206 // Used to track the state of a call to view->Embed().
207 struct EmbedDetails
{
209 : callback_run(false),
213 connection(nullptr) {}
215 // The callback supplied to Embed() was received.
218 // The boolean supplied to the Embed() callback.
221 // Whether a MessageLoop is running.
224 // Connection id supplied to the Embed() callback.
225 ConnectionSpecificId connection_id
;
227 // The ViewTreeConnection that resulted from the Embed(). null if |result|
229 ViewTreeConnection
* connection
;
232 void EmbedCallbackImpl(bool result
, ConnectionSpecificId connection_id
) {
233 embed_details_
->callback_run
= true;
234 embed_details_
->result
= result
;
235 embed_details_
->connection_id
= connection_id
;
236 if (embed_details_
->waiting
&& (!result
|| embed_details_
->connection
))
237 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
240 scoped_ptr
<EmbedDetails
> embed_details_
;
242 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTest
);
245 TEST_F(ViewManagerTest
, RootView
) {
246 ASSERT_NE(nullptr, window_manager());
247 EXPECT_NE(nullptr, window_manager()->GetRoot());
250 TEST_F(ViewManagerTest
, Embed
) {
251 View
* view
= window_manager()->CreateView();
252 ASSERT_NE(nullptr, view
);
253 view
->SetVisible(true);
254 window_manager()->GetRoot()->AddChild(view
);
255 ViewTreeConnection
* embedded
= Embed(view
).connection
;
256 ASSERT_NE(nullptr, embedded
);
258 View
* view_in_embedded
= embedded
->GetRoot();
259 ASSERT_NE(nullptr, view_in_embedded
);
260 EXPECT_EQ(view
->id(), view_in_embedded
->id());
261 EXPECT_EQ(nullptr, view_in_embedded
->parent());
262 EXPECT_TRUE(view_in_embedded
->children().empty());
265 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see
267 TEST_F(ViewManagerTest
, EmbeddedDoesntSeeChild
) {
268 View
* view
= window_manager()->CreateView();
269 ASSERT_NE(nullptr, view
);
270 view
->SetVisible(true);
271 window_manager()->GetRoot()->AddChild(view
);
272 View
* nested
= window_manager()->CreateView();
273 ASSERT_NE(nullptr, nested
);
274 nested
->SetVisible(true);
275 view
->AddChild(nested
);
277 ViewTreeConnection
* embedded
= Embed(view
).connection
;
278 ASSERT_NE(nullptr, embedded
);
279 View
* view_in_embedded
= embedded
->GetRoot();
280 EXPECT_EQ(view
->id(), view_in_embedded
->id());
281 EXPECT_EQ(nullptr, view_in_embedded
->parent());
282 EXPECT_TRUE(view_in_embedded
->children().empty());
285 // TODO(beng): write a replacement test for the one that once existed here:
286 // This test validates the following scenario:
287 // - a view originating from one connection
288 // - a view originating from a second connection
289 // + the connection originating the view is destroyed
290 // -> the view should still exist (since the second connection is live) but
291 // should be disconnected from any views.
292 // http://crbug.com/396300
294 // TODO(beng): The new test should validate the scenario as described above
295 // except that the second connection still has a valid tree.
297 // Verifies that bounds changes applied to a view hierarchy in one connection
298 // are reflected to another.
299 TEST_F(ViewManagerTest
, SetBounds
) {
300 View
* view
= window_manager()->CreateView();
301 view
->SetVisible(true);
302 window_manager()->GetRoot()->AddChild(view
);
303 ViewTreeConnection
* embedded
= Embed(view
).connection
;
304 ASSERT_NE(nullptr, embedded
);
306 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
307 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
310 rect
.width
= rect
.height
= 100;
311 view
->SetBounds(rect
);
312 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded
));
313 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
316 // Verifies that bounds changes applied to a view owned by a different
317 // connection are refused.
318 TEST_F(ViewManagerTest
, SetBoundsSecurity
) {
319 View
* view
= window_manager()->CreateView();
320 view
->SetVisible(true);
321 window_manager()->GetRoot()->AddChild(view
);
322 ViewTreeConnection
* embedded
= Embed(view
).connection
;
323 ASSERT_NE(nullptr, embedded
);
325 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
329 view
->SetBounds(rect
);
330 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded
));
334 view_in_embedded
->SetBounds(rect
);
335 // Bounds change should have been rejected.
336 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
339 // Verifies that a view can only be destroyed by the connection that created it.
340 TEST_F(ViewManagerTest
, DestroySecurity
) {
341 View
* view
= window_manager()->CreateView();
342 view
->SetVisible(true);
343 window_manager()->GetRoot()->AddChild(view
);
344 ViewTreeConnection
* embedded
= Embed(view
).connection
;
345 ASSERT_NE(nullptr, embedded
);
347 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
349 ViewTracker
tracker2(view_in_embedded
);
350 view_in_embedded
->Destroy();
351 // View should not have been destroyed.
352 EXPECT_TRUE(tracker2
.is_valid());
354 ViewTracker
tracker1(view
);
356 EXPECT_FALSE(tracker1
.is_valid());
359 TEST_F(ViewManagerTest
, MultiRoots
) {
360 View
* view1
= window_manager()->CreateView();
361 view1
->SetVisible(true);
362 window_manager()->GetRoot()->AddChild(view1
);
363 View
* view2
= window_manager()->CreateView();
364 view2
->SetVisible(true);
365 window_manager()->GetRoot()->AddChild(view2
);
366 ViewTreeConnection
* embedded1
= Embed(view1
).connection
;
367 ASSERT_NE(nullptr, embedded1
);
368 ViewTreeConnection
* embedded2
= Embed(view2
).connection
;
369 ASSERT_NE(nullptr, embedded2
);
370 EXPECT_NE(embedded1
, embedded2
);
373 // TODO(alhaad): Currently, the RunLoop gets stuck waiting for order change.
374 // Debug and re-enable this.
375 TEST_F(ViewManagerTest
, DISABLED_Reorder
) {
376 View
* view1
= window_manager()->CreateView();
377 view1
->SetVisible(true);
378 window_manager()->GetRoot()->AddChild(view1
);
380 ViewTreeConnection
* embedded
= Embed(view1
).connection
;
381 ASSERT_NE(nullptr, embedded
);
383 View
* view11
= embedded
->CreateView();
384 view11
->SetVisible(true);
385 embedded
->GetRoot()->AddChild(view11
);
386 View
* view12
= embedded
->CreateView();
387 view12
->SetVisible(true);
388 embedded
->GetRoot()->AddChild(view12
);
390 View
* root_in_embedded
= embedded
->GetRoot();
393 ASSERT_TRUE(WaitForTreeSizeToMatch(root_in_embedded
, 3u));
394 view11
->MoveToFront();
395 ASSERT_TRUE(WaitForOrderChange(embedded
, root_in_embedded
));
397 EXPECT_EQ(root_in_embedded
->children().front(),
398 embedded
->GetViewById(view12
->id()));
399 EXPECT_EQ(root_in_embedded
->children().back(),
400 embedded
->GetViewById(view11
->id()));
404 view11
->MoveToBack();
405 ASSERT_TRUE(WaitForOrderChange(embedded
,
406 embedded
->GetViewById(view11
->id())));
408 EXPECT_EQ(root_in_embedded
->children().front(),
409 embedded
->GetViewById(view11
->id()));
410 EXPECT_EQ(root_in_embedded
->children().back(),
411 embedded
->GetViewById(view12
->id()));
417 class VisibilityChangeObserver
: public ViewObserver
{
419 explicit VisibilityChangeObserver(View
* view
) : view_(view
) {
420 view_
->AddObserver(this);
422 ~VisibilityChangeObserver() override
{ view_
->RemoveObserver(this); }
425 // Overridden from ViewObserver:
426 void OnViewVisibilityChanged(View
* view
) override
{
427 EXPECT_EQ(view
, view_
);
428 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
433 MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver
);
438 TEST_F(ViewManagerTest
, Visible
) {
439 View
* view1
= window_manager()->CreateView();
440 view1
->SetVisible(true);
441 window_manager()->GetRoot()->AddChild(view1
);
443 // Embed another app and verify initial state.
444 ViewTreeConnection
* embedded
= Embed(view1
).connection
;
445 ASSERT_NE(nullptr, embedded
);
446 ASSERT_NE(nullptr, embedded
->GetRoot());
447 View
* embedded_root
= embedded
->GetRoot();
448 EXPECT_TRUE(embedded_root
->visible());
449 EXPECT_TRUE(embedded_root
->IsDrawn());
451 // Change the visible state from the first connection and verify its mirrored
452 // correctly to the embedded app.
454 VisibilityChangeObserver
observer(embedded_root
);
455 view1
->SetVisible(false);
456 ASSERT_TRUE(ViewManagerTestBase::DoRunLoopWithTimeout());
459 EXPECT_FALSE(view1
->visible());
460 EXPECT_FALSE(view1
->IsDrawn());
462 EXPECT_FALSE(embedded_root
->visible());
463 EXPECT_FALSE(embedded_root
->IsDrawn());
465 // Make the node visible again.
467 VisibilityChangeObserver
observer(embedded_root
);
468 view1
->SetVisible(true);
469 ASSERT_TRUE(ViewManagerTestBase::DoRunLoopWithTimeout());
472 EXPECT_TRUE(view1
->visible());
473 EXPECT_TRUE(view1
->IsDrawn());
475 EXPECT_TRUE(embedded_root
->visible());
476 EXPECT_TRUE(embedded_root
->IsDrawn());
481 class DrawnChangeObserver
: public ViewObserver
{
483 explicit DrawnChangeObserver(View
* view
) : view_(view
) {
484 view_
->AddObserver(this);
486 ~DrawnChangeObserver() override
{ view_
->RemoveObserver(this); }
489 // Overridden from ViewObserver:
490 void OnViewDrawnChanged(View
* view
) override
{
491 EXPECT_EQ(view
, view_
);
492 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
497 MOJO_DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver
);
502 TEST_F(ViewManagerTest
, Drawn
) {
503 View
* view1
= window_manager()->CreateView();
504 view1
->SetVisible(true);
505 window_manager()->GetRoot()->AddChild(view1
);
507 // Embed another app and verify initial state.
508 ViewTreeConnection
* embedded
= Embed(view1
).connection
;
509 ASSERT_NE(nullptr, embedded
);
510 ASSERT_NE(nullptr, embedded
->GetRoot());
511 View
* embedded_root
= embedded
->GetRoot();
512 EXPECT_TRUE(embedded_root
->visible());
513 EXPECT_TRUE(embedded_root
->IsDrawn());
515 // Change the visibility of the root, this should propagate a drawn state
516 // change to |embedded|.
518 DrawnChangeObserver
observer(embedded_root
);
519 window_manager()->GetRoot()->SetVisible(false);
520 ASSERT_TRUE(DoRunLoopWithTimeout());
523 EXPECT_TRUE(view1
->visible());
524 EXPECT_FALSE(view1
->IsDrawn());
526 EXPECT_TRUE(embedded_root
->visible());
527 EXPECT_FALSE(embedded_root
->IsDrawn());
530 // TODO(beng): tests for view event dispatcher.
531 // - verify that we see events for all views.
535 class FocusChangeObserver
: public ViewObserver
{
537 explicit FocusChangeObserver(View
* view
)
538 : view_(view
), last_gained_focus_(nullptr), last_lost_focus_(nullptr) {
539 view_
->AddObserver(this);
541 ~FocusChangeObserver() override
{ view_
->RemoveObserver(this); }
543 View
* last_gained_focus() { return last_gained_focus_
; }
545 View
* last_lost_focus() { return last_lost_focus_
; }
548 // Overridden from ViewObserver.
549 void OnViewFocusChanged(View
* gained_focus
, View
* lost_focus
) override
{
550 EXPECT_TRUE(!gained_focus
|| gained_focus
->HasFocus());
551 EXPECT_FALSE(lost_focus
&& lost_focus
->HasFocus());
552 last_gained_focus_
= gained_focus
;
553 last_lost_focus_
= lost_focus
;
554 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
558 View
* last_gained_focus_
;
559 View
* last_lost_focus_
;
561 MOJO_DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver
);
566 TEST_F(ViewManagerTest
, Focus
) {
567 View
* view1
= window_manager()->CreateView();
568 view1
->SetVisible(true);
569 window_manager()->GetRoot()->AddChild(view1
);
571 ViewTreeConnection
* embedded
= Embed(view1
).connection
;
572 ASSERT_NE(nullptr, embedded
);
573 View
* view11
= embedded
->CreateView();
574 view11
->SetVisible(true);
575 embedded
->GetRoot()->AddChild(view11
);
577 // TODO(alhaad): Figure out why switching focus between views from different
578 // connections is causing the tests to crash and add tests for that.
580 View
* embedded_root
= embedded
->GetRoot();
581 FocusChangeObserver
observer(embedded_root
);
582 embedded_root
->SetFocus();
583 ASSERT_TRUE(DoRunLoopWithTimeout());
584 ASSERT_NE(nullptr, observer
.last_gained_focus());
585 EXPECT_EQ(embedded_root
->id(), observer
.last_gained_focus()->id());
588 FocusChangeObserver
observer(view11
);
590 ASSERT_TRUE(DoRunLoopWithTimeout());
591 ASSERT_NE(nullptr, observer
.last_gained_focus());
592 ASSERT_NE(nullptr, observer
.last_lost_focus());
593 EXPECT_EQ(view11
->id(), observer
.last_gained_focus()->id());
594 EXPECT_EQ(embedded
->GetRoot()->id(), observer
.last_lost_focus()->id());
597 // Add an observer on the View that loses focus, and make sure the observer
598 // sees the right values.
599 FocusChangeObserver
observer(view11
);
600 embedded
->GetRoot()->SetFocus();
601 ASSERT_TRUE(DoRunLoopWithTimeout());
602 ASSERT_NE(nullptr, observer
.last_gained_focus());
603 ASSERT_NE(nullptr, observer
.last_lost_focus());
604 EXPECT_EQ(view11
->id(), observer
.last_lost_focus()->id());
605 EXPECT_EQ(embedded
->GetRoot()->id(), observer
.last_gained_focus()->id());
611 class DestroyedChangedObserver
: public ViewObserver
{
613 DestroyedChangedObserver(ViewManagerTestBase
* test
,
616 : test_(test
), view_(view
), got_destroy_(got_destroy
) {
617 view_
->AddObserver(this);
619 ~DestroyedChangedObserver() override
{
621 view_
->RemoveObserver(this);
625 // Overridden from ViewObserver:
626 void OnViewDestroyed(View
* view
) override
{
627 EXPECT_EQ(view
, view_
);
628 view_
->RemoveObserver(this);
629 *got_destroy_
= true;
632 // We should always get OnViewDestroyed() before OnConnectionLost().
633 EXPECT_FALSE(test_
->view_tree_connection_destroyed());
636 ViewManagerTestBase
* test_
;
640 MOJO_DISALLOW_COPY_AND_ASSIGN(DestroyedChangedObserver
);
645 // Verifies deleting a ViewManager sends the right notifications.
646 TEST_F(ViewManagerTest
, DeleteViewManager
) {
647 View
* view
= window_manager()->CreateView();
648 ASSERT_NE(nullptr, view
);
649 view
->SetVisible(true);
650 window_manager()->GetRoot()->AddChild(view
);
651 ViewTreeConnection
* connection
= Embed(view
).connection
;
652 ASSERT_TRUE(connection
);
653 bool got_destroy
= false;
654 DestroyedChangedObserver
observer(this, connection
->GetRoot(),
657 EXPECT_TRUE(view_tree_connection_destroyed());
658 EXPECT_TRUE(got_destroy
);
661 // Verifies two Embed()s in the same view trigger deletion of the first
663 TEST_F(ViewManagerTest
, DisconnectTriggersDelete
) {
664 View
* view
= window_manager()->CreateView();
665 ASSERT_NE(nullptr, view
);
666 view
->SetVisible(true);
667 window_manager()->GetRoot()->AddChild(view
);
668 ViewTreeConnection
* connection
= Embed(view
).connection
;
669 EXPECT_NE(connection
, window_manager());
670 View
* embedded_view
= connection
->CreateView();
671 // Embed again, this should trigger disconnect and deletion of connection.
673 DestroyedChangedObserver
observer(this, embedded_view
, &got_destroy
);
674 EXPECT_FALSE(view_tree_connection_destroyed());
676 EXPECT_TRUE(view_tree_connection_destroyed());
679 class ViewRemovedFromParentObserver
: public ViewObserver
{
681 explicit ViewRemovedFromParentObserver(View
* view
)
682 : view_(view
), was_removed_(false) {
683 view_
->AddObserver(this);
685 ~ViewRemovedFromParentObserver() override
{ view_
->RemoveObserver(this); }
687 bool was_removed() const { return was_removed_
; }
690 // Overridden from ViewObserver:
691 void OnTreeChanged(const TreeChangeParams
& params
) override
{
692 if (params
.target
== view_
&& !params
.new_parent
)
699 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewRemovedFromParentObserver
);
702 TEST_F(ViewManagerTest
, EmbedRemovesChildren
) {
703 View
* view1
= window_manager()->CreateView();
704 View
* view2
= window_manager()->CreateView();
705 window_manager()->GetRoot()->AddChild(view1
);
706 view1
->AddChild(view2
);
708 ViewRemovedFromParentObserver
observer(view2
);
709 view1
->Embed(ConnectToApplicationAndGetViewManagerClient());
710 EXPECT_TRUE(observer
.was_removed());
711 EXPECT_EQ(nullptr, view2
->parent());
712 EXPECT_TRUE(view1
->children().empty());
714 // Run the message loop so the Embed() call above completes. Without this
715 // we may end up reconnecting to the test and rerunning the test, which is
716 // problematic since the other services don't shut down.
717 ASSERT_TRUE(DoRunLoopWithTimeout());
722 class DestroyObserver
: public ViewObserver
{
724 DestroyObserver(ViewManagerTestBase
* test
,
725 ViewTreeConnection
* connection
,
727 : test_(test
), got_destroy_(got_destroy
) {
728 connection
->GetRoot()->AddObserver(this);
730 ~DestroyObserver() override
{}
733 // Overridden from ViewObserver:
734 void OnViewDestroyed(View
* view
) override
{
735 *got_destroy_
= true;
736 view
->RemoveObserver(this);
738 // We should always get OnViewDestroyed() before OnViewManagerDestroyed().
739 EXPECT_FALSE(test_
->view_tree_connection_destroyed());
741 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
744 ViewManagerTestBase
* test_
;
747 MOJO_DISALLOW_COPY_AND_ASSIGN(DestroyObserver
);
752 // Verifies deleting a View that is the root of another connection notifies
753 // observers in the right order (OnViewDestroyed() before
754 // OnViewManagerDestroyed()).
755 TEST_F(ViewManagerTest
, ViewManagerDestroyedAfterRootObserver
) {
756 View
* embed_view
= window_manager()->CreateView();
757 window_manager()->GetRoot()->AddChild(embed_view
);
759 ViewTreeConnection
* embedded_connection
= Embed(embed_view
).connection
;
761 bool got_destroy
= false;
762 DestroyObserver
observer(this, embedded_connection
, &got_destroy
);
763 // Delete the view |embedded_connection| is embedded in. This is async,
764 // but will eventually trigger deleting |embedded_connection|.
765 embed_view
->Destroy();
766 EXPECT_TRUE(DoRunLoopWithTimeout());
767 EXPECT_TRUE(got_destroy
);
770 // Verifies an embed root sees views created beneath it from another
772 TEST_F(ViewManagerTest
, EmbedRootSeesHierarchyChanged
) {
773 View
* embed_view
= window_manager()->CreateView();
774 window_manager()->GetRoot()->AddChild(embed_view
);
776 embed_view
->SetAccessPolicy(ViewTree::ACCESS_POLICY_EMBED_ROOT
);
777 ViewTreeConnection
* vm2
= Embed(embed_view
).connection
;
778 View
* vm2_v1
= vm2
->CreateView();
779 vm2
->GetRoot()->AddChild(vm2_v1
);
781 ViewTreeConnection
* vm3
= Embed(vm2_v1
).connection
;
782 View
* vm3_v1
= vm3
->CreateView();
783 vm3
->GetRoot()->AddChild(vm3_v1
);
785 // As |vm2| is an embed root it should get notified about |vm3_v1|.
786 ASSERT_TRUE(WaitForTreeSizeToMatch(vm2_v1
, 2));
789 TEST_F(ViewManagerTest
, EmbedFromEmbedRoot
) {
790 View
* embed_view
= window_manager()->CreateView();
791 window_manager()->GetRoot()->AddChild(embed_view
);
793 // Give the connection embedded at |embed_view| embed root powers.
794 embed_view
->SetAccessPolicy(ViewTree::ACCESS_POLICY_EMBED_ROOT
);
795 const EmbedResult result1
= Embed(embed_view
);
796 ViewTreeConnection
* vm2
= result1
.connection
;
797 EXPECT_EQ(result1
.connection_id
, vm2
->GetConnectionId());
798 View
* vm2_v1
= vm2
->CreateView();
799 vm2
->GetRoot()->AddChild(vm2_v1
);
801 const EmbedResult result2
= Embed(vm2_v1
);
802 ViewTreeConnection
* vm3
= result2
.connection
;
803 EXPECT_EQ(result2
.connection_id
, vm3
->GetConnectionId());
804 View
* vm3_v1
= vm3
->CreateView();
805 vm3
->GetRoot()->AddChild(vm3_v1
);
807 // Embed from v3, the callback should not get the connection id as vm3 is not
809 const EmbedResult result3
= Embed(vm3_v1
);
810 ASSERT_TRUE(result3
.connection
);
811 EXPECT_EQ(0, result3
.connection_id
);
813 // As |vm2| is an embed root it should get notified about |vm3_v1|.
814 ASSERT_TRUE(WaitForTreeSizeToMatch(vm2_v1
, 2));
816 // Embed() from vm2 in vm3_v1. This is allowed as vm2 is an embed root, and
817 // further the callback should see the connection id.
818 ASSERT_EQ(1u, vm2_v1
->children().size());
819 View
* vm3_v1_in_vm2
= vm2_v1
->children()[0];
820 const EmbedResult result4
= Embed(vm3_v1_in_vm2
);
821 ASSERT_TRUE(result4
.connection
);
822 EXPECT_EQ(result4
.connection_id
, result4
.connection
->GetConnectionId());