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.
5 #include "components/view_manager/public/cpp/view_manager.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_vector.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/test/test_timeouts.h"
14 #include "components/view_manager/public/cpp/lib/view_manager_client_impl.h"
15 #include "components/view_manager/public/cpp/view_manager_client_factory.h"
16 #include "components/view_manager/public/cpp/view_manager_delegate.h"
17 #include "components/view_manager/public/cpp/view_manager_init.h"
18 #include "components/view_manager/public/cpp/view_observer.h"
19 #include "mojo/application/public/cpp/application_connection.h"
20 #include "mojo/application/public/cpp/application_delegate.h"
21 #include "mojo/application/public/cpp/application_impl.h"
22 #include "mojo/application/public/cpp/application_test_base.h"
23 #include "mojo/application/public/cpp/service_provider_impl.h"
24 #include "ui/mojo/geometry/geometry_util.h"
30 base::RunLoop
* current_run_loop
= nullptr;
32 void TimeoutRunLoop(const base::Closure
& timeout_task
, bool* timeout
) {
33 CHECK(current_run_loop
);
38 bool DoRunLoopWithTimeout() {
39 if (current_run_loop
!= nullptr)
43 base::RunLoop run_loop
;
44 base::MessageLoop::current()->PostDelayedTask(
45 FROM_HERE
, base::Bind(&TimeoutRunLoop
, run_loop
.QuitClosure(), &timeout
),
46 TestTimeouts::action_timeout());
48 current_run_loop
= &run_loop
;
49 current_run_loop
->Run();
50 current_run_loop
= nullptr;
55 current_run_loop
->Quit();
56 current_run_loop
= nullptr;
59 class BoundsChangeObserver
: public ViewObserver
{
61 explicit BoundsChangeObserver(View
* view
) : view_(view
) {
62 view_
->AddObserver(this);
64 ~BoundsChangeObserver() override
{ view_
->RemoveObserver(this); }
67 // Overridden from ViewObserver:
68 void OnViewBoundsChanged(View
* view
,
69 const Rect
& old_bounds
,
70 const Rect
& new_bounds
) override
{
71 DCHECK_EQ(view
, view_
);
77 MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver
);
80 // Wait until the bounds of the supplied view change; returns false on timeout.
81 bool WaitForBoundsToChange(View
* view
) {
82 BoundsChangeObserver
observer(view
);
83 return DoRunLoopWithTimeout();
86 // Increments the width of |view| and waits for a bounds change in |other_vm|s
88 bool IncrementWidthAndWaitForChange(View
* view
, ViewManager
* other_vm
) {
89 mojo::Rect bounds
= view
->bounds();
91 view
->SetBounds(bounds
);
92 View
* view_in_vm
= other_vm
->GetRoot();
93 if (view_in_vm
== view
|| view_in_vm
->id() != view
->id())
95 return WaitForBoundsToChange(view_in_vm
);
98 // Spins a run loop until the tree beginning at |root| has |tree_size| views
99 // (including |root|).
100 class TreeSizeMatchesObserver
: public ViewObserver
{
102 TreeSizeMatchesObserver(View
* tree
, size_t tree_size
)
103 : tree_(tree
), tree_size_(tree_size
) {
104 tree_
->AddObserver(this);
106 ~TreeSizeMatchesObserver() override
{ tree_
->RemoveObserver(this); }
108 bool IsTreeCorrectSize() { return CountViews(tree_
) == tree_size_
; }
111 // Overridden from ViewObserver:
112 void OnTreeChanged(const TreeChangeParams
& params
) override
{
113 if (IsTreeCorrectSize())
117 size_t CountViews(const View
* view
) const {
119 View::Children::const_iterator it
= view
->children().begin();
120 for (; it
!= view
->children().end(); ++it
)
121 count
+= CountViews(*it
);
128 MOJO_DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver
);
131 // Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
132 bool WaitForTreeSizeToMatch(View
* view
, size_t tree_size
) {
133 TreeSizeMatchesObserver
observer(view
, tree_size
);
134 return observer
.IsTreeCorrectSize() || DoRunLoopWithTimeout();
137 class OrderChangeObserver
: public ViewObserver
{
139 OrderChangeObserver(View
* view
) : view_(view
) { view_
->AddObserver(this); }
140 ~OrderChangeObserver() override
{ view_
->RemoveObserver(this); }
143 // Overridden from ViewObserver:
144 void OnViewReordered(View
* view
,
146 OrderDirection direction
) override
{
147 DCHECK_EQ(view
, view_
);
153 MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver
);
156 // Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
157 bool WaitForOrderChange(ViewManager
* view_manager
, View
* view
) {
158 OrderChangeObserver
observer(view
);
159 return DoRunLoopWithTimeout();
162 // Tracks a view's destruction. Query is_valid() for current state.
163 class ViewTracker
: public ViewObserver
{
165 explicit ViewTracker(View
* view
) : view_(view
) { view_
->AddObserver(this); }
166 ~ViewTracker() override
{
168 view_
->RemoveObserver(this);
171 bool is_valid() const { return !!view_
; }
174 // Overridden from ViewObserver:
175 void OnViewDestroyed(View
* view
) override
{
176 DCHECK_EQ(view
, view_
);
183 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTracker
);
188 // ViewManager -----------------------------------------------------------------
190 // These tests model synchronization of two peer connections to the view manager
191 // service, that are given access to some root view.
193 class ViewManagerTest
: public test::ApplicationTestBase
,
194 public ApplicationDelegate
,
195 public ViewManagerDelegate
{
198 : most_recent_view_manager_(nullptr),
199 window_manager_(nullptr),
200 got_disconnect_(false),
201 on_will_embed_count_(0u),
202 on_will_embed_return_value_(true) {}
204 void clear_on_will_embed_count() { on_will_embed_count_
= 0u; }
205 size_t on_will_embed_count() const { return on_will_embed_count_
; }
207 void set_on_will_embed_return_value(bool value
) {
208 on_will_embed_return_value_
= value
;
211 ViewManager
* most_recent_view_manager() { return most_recent_view_manager_
; }
213 // Overridden from ApplicationDelegate:
214 void Initialize(ApplicationImpl
* app
) override
{
215 view_manager_client_factory_
.reset(
216 new ViewManagerClientFactory(app
->shell(), this));
219 // ApplicationDelegate implementation.
220 bool ConfigureIncomingConnection(ApplicationConnection
* connection
) override
{
221 connection
->AddService(view_manager_client_factory_
.get());
225 ViewManager
* window_manager() { return window_manager_
; }
227 // Embeds another version of the test app @ view. This runs a run loop until
228 // a response is received, or a timeout. On success the new ViewManager is
230 ViewManager
* Embed(View
* view
) {
231 return EmbedImpl(view
, EmbedType::NO_REEMBED
);
234 // Same as Embed(), but uses EmbedAllowingReembed().
235 ViewManager
* EmbedAllowingReembed(View
* view
) {
236 return EmbedImpl(view
, EmbedType::ALLOW_REEMBED
);
239 // Establishes a connection to this application and asks for a
240 // ViewManagerClient. The ViewManagerClient is then embedded in |view|.
241 // This does *not* wait for the connection to complete.
242 void ConnectToApplicationAndEmbed(View
* view
) {
243 mojo::URLRequestPtr
request(mojo::URLRequest::New());
244 request
->url
= mojo::String::From(application_impl()->url());
245 ApplicationConnection
* connection
=
246 application_impl()->ConnectToApplication(request
.Pass());
247 mojo::ViewManagerClientPtr client
;
248 connection
->ConnectToService(&client
);
249 view
->Embed(client
.Pass());
252 bool got_disconnect() const { return got_disconnect_
; }
254 ApplicationDelegate
* GetApplicationDelegate() override
{ return this; }
256 // Overridden from ViewManagerDelegate:
257 void OnEmbed(View
* root
) override
{
258 most_recent_view_manager_
= root
->view_manager();
261 void OnEmbedForDescendant(View
* view
,
262 mojo::URLRequestPtr request
,
263 mojo::ViewManagerClientPtr
* client
) override
{
264 on_will_embed_count_
++;
265 if (on_will_embed_return_value_
) {
266 ApplicationConnection
* connection
=
267 application_impl()->ConnectToApplication(request
.Pass());
268 connection
->ConnectToService(client
);
273 void OnViewManagerDestroyed(ViewManager
* view_manager
) override
{
274 got_disconnect_
= true;
278 enum class EmbedType
{
283 ViewManager
* EmbedImpl(View
* view
, EmbedType type
) {
284 most_recent_view_manager_
= nullptr;
285 if (type
== EmbedType::ALLOW_REEMBED
) {
286 mojo::URLRequestPtr
request(mojo::URLRequest::New());
287 request
->url
= mojo::String::From(application_impl()->url());
288 view
->EmbedAllowingReembed(request
.Pass());
290 ConnectToApplicationAndEmbed(view
);
292 if (!DoRunLoopWithTimeout())
294 ViewManager
* vm
= nullptr;
295 std::swap(vm
, most_recent_view_manager_
);
299 // Overridden from testing::Test:
300 void SetUp() override
{
301 ApplicationTestBase::SetUp();
303 view_manager_init_
.reset(
304 new ViewManagerInit(application_impl(), this, nullptr));
305 ASSERT_TRUE(DoRunLoopWithTimeout());
306 std::swap(window_manager_
, most_recent_view_manager_
);
309 // Overridden from testing::Test:
310 void TearDown() override
{
311 view_manager_init_
.reset(); // Uses application_impl() from base class.
312 ApplicationTestBase::TearDown();
315 scoped_ptr
<ViewManagerInit
> view_manager_init_
;
317 scoped_ptr
<ViewManagerClientFactory
> view_manager_client_factory_
;
319 // Used to receive the most recent view manager loaded by an embed action.
320 ViewManager
* most_recent_view_manager_
;
321 // The View Manager connection held by the window manager (app running at the
323 ViewManager
* window_manager_
;
325 bool got_disconnect_
;
327 // Number of times OnWillEmbed() has been called.
328 size_t on_will_embed_count_
;
330 // Value OnWillEmbed() should return.
331 bool on_will_embed_return_value_
;
333 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTest
);
336 TEST_F(ViewManagerTest
, RootView
) {
337 ASSERT_NE(nullptr, window_manager());
338 EXPECT_NE(nullptr, window_manager()->GetRoot());
341 TEST_F(ViewManagerTest
, Embed
) {
342 View
* view
= window_manager()->CreateView();
343 ASSERT_NE(nullptr, view
);
344 view
->SetVisible(true);
345 window_manager()->GetRoot()->AddChild(view
);
346 ViewManager
* embedded
= Embed(view
);
347 ASSERT_NE(nullptr, embedded
);
349 View
* view_in_embedded
= embedded
->GetRoot();
350 ASSERT_NE(nullptr, view_in_embedded
);
351 EXPECT_EQ(view
->id(), view_in_embedded
->id());
352 EXPECT_EQ(nullptr, view_in_embedded
->parent());
353 EXPECT_TRUE(view_in_embedded
->children().empty());
356 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see
358 TEST_F(ViewManagerTest
, EmbeddedDoesntSeeChild
) {
359 View
* view
= window_manager()->CreateView();
360 ASSERT_NE(nullptr, view
);
361 view
->SetVisible(true);
362 window_manager()->GetRoot()->AddChild(view
);
363 View
* nested
= window_manager()->CreateView();
364 ASSERT_NE(nullptr, nested
);
365 nested
->SetVisible(true);
366 view
->AddChild(nested
);
368 ViewManager
* embedded
= Embed(view
);
369 ASSERT_NE(nullptr, embedded
);
370 View
* view_in_embedded
= embedded
->GetRoot();
371 EXPECT_EQ(view
->id(), view_in_embedded
->id());
372 EXPECT_EQ(nullptr, view_in_embedded
->parent());
373 EXPECT_TRUE(view_in_embedded
->children().empty());
376 // TODO(beng): write a replacement test for the one that once existed here:
377 // This test validates the following scenario:
378 // - a view originating from one connection
379 // - a view originating from a second connection
380 // + the connection originating the view is destroyed
381 // -> the view should still exist (since the second connection is live) but
382 // should be disconnected from any views.
383 // http://crbug.com/396300
385 // TODO(beng): The new test should validate the scenario as described above
386 // except that the second connection still has a valid tree.
388 // Verifies that bounds changes applied to a view hierarchy in one connection
389 // are reflected to another.
390 TEST_F(ViewManagerTest
, SetBounds
) {
391 View
* view
= window_manager()->CreateView();
392 view
->SetVisible(true);
393 window_manager()->GetRoot()->AddChild(view
);
394 ViewManager
* embedded
= Embed(view
);
395 ASSERT_NE(nullptr, embedded
);
397 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
398 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
401 rect
.width
= rect
.height
= 100;
402 view
->SetBounds(rect
);
403 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded
));
404 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
407 // Verifies that bounds changes applied to a view owned by a different
408 // connection are refused.
409 TEST_F(ViewManagerTest
, SetBoundsSecurity
) {
410 View
* view
= window_manager()->CreateView();
411 view
->SetVisible(true);
412 window_manager()->GetRoot()->AddChild(view
);
413 ViewManager
* embedded
= Embed(view
);
414 ASSERT_NE(nullptr, embedded
);
416 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
420 view
->SetBounds(rect
);
421 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded
));
425 view_in_embedded
->SetBounds(rect
);
426 // Bounds change should have been rejected.
427 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
430 // Verifies that a view can only be destroyed by the connection that created it.
431 TEST_F(ViewManagerTest
, DestroySecurity
) {
432 View
* view
= window_manager()->CreateView();
433 view
->SetVisible(true);
434 window_manager()->GetRoot()->AddChild(view
);
435 ViewManager
* embedded
= Embed(view
);
436 ASSERT_NE(nullptr, embedded
);
438 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
440 ViewTracker
tracker2(view_in_embedded
);
441 view_in_embedded
->Destroy();
442 // View should not have been destroyed.
443 EXPECT_TRUE(tracker2
.is_valid());
445 ViewTracker
tracker1(view
);
447 EXPECT_FALSE(tracker1
.is_valid());
450 TEST_F(ViewManagerTest
, MultiRoots
) {
451 View
* view1
= window_manager()->CreateView();
452 view1
->SetVisible(true);
453 window_manager()->GetRoot()->AddChild(view1
);
454 View
* view2
= window_manager()->CreateView();
455 view2
->SetVisible(true);
456 window_manager()->GetRoot()->AddChild(view2
);
457 ViewManager
* embedded1
= Embed(view1
);
458 ASSERT_NE(nullptr, embedded1
);
459 ViewManager
* embedded2
= Embed(view2
);
460 ASSERT_NE(nullptr, embedded2
);
461 EXPECT_NE(embedded1
, embedded2
);
464 // TODO(alhaad): Currently, the RunLoop gets stuck waiting for order change.
465 // Debug and re-enable this.
466 TEST_F(ViewManagerTest
, DISABLED_Reorder
) {
467 View
* view1
= window_manager()->CreateView();
468 view1
->SetVisible(true);
469 window_manager()->GetRoot()->AddChild(view1
);
471 ViewManager
* embedded
= Embed(view1
);
472 ASSERT_NE(nullptr, embedded
);
474 View
* view11
= embedded
->CreateView();
475 view11
->SetVisible(true);
476 embedded
->GetRoot()->AddChild(view11
);
477 View
* view12
= embedded
->CreateView();
478 view12
->SetVisible(true);
479 embedded
->GetRoot()->AddChild(view12
);
481 View
* root_in_embedded
= embedded
->GetRoot();
484 ASSERT_TRUE(WaitForTreeSizeToMatch(root_in_embedded
, 3u));
485 view11
->MoveToFront();
486 ASSERT_TRUE(WaitForOrderChange(embedded
, root_in_embedded
));
488 EXPECT_EQ(root_in_embedded
->children().front(),
489 embedded
->GetViewById(view12
->id()));
490 EXPECT_EQ(root_in_embedded
->children().back(),
491 embedded
->GetViewById(view11
->id()));
495 view11
->MoveToBack();
496 ASSERT_TRUE(WaitForOrderChange(embedded
,
497 embedded
->GetViewById(view11
->id())));
499 EXPECT_EQ(root_in_embedded
->children().front(),
500 embedded
->GetViewById(view11
->id()));
501 EXPECT_EQ(root_in_embedded
->children().back(),
502 embedded
->GetViewById(view12
->id()));
508 class VisibilityChangeObserver
: public ViewObserver
{
510 explicit VisibilityChangeObserver(View
* view
) : view_(view
) {
511 view_
->AddObserver(this);
513 ~VisibilityChangeObserver() override
{ view_
->RemoveObserver(this); }
516 // Overridden from ViewObserver:
517 void OnViewVisibilityChanged(View
* view
) override
{
518 EXPECT_EQ(view
, view_
);
524 MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver
);
529 TEST_F(ViewManagerTest
, Visible
) {
530 View
* view1
= window_manager()->CreateView();
531 view1
->SetVisible(true);
532 window_manager()->GetRoot()->AddChild(view1
);
534 // Embed another app and verify initial state.
535 ViewManager
* embedded
= Embed(view1
);
536 ASSERT_NE(nullptr, embedded
);
537 ASSERT_NE(nullptr, embedded
->GetRoot());
538 View
* embedded_root
= embedded
->GetRoot();
539 EXPECT_TRUE(embedded_root
->visible());
540 EXPECT_TRUE(embedded_root
->IsDrawn());
542 // Change the visible state from the first connection and verify its mirrored
543 // correctly to the embedded app.
545 VisibilityChangeObserver
observer(embedded_root
);
546 view1
->SetVisible(false);
547 ASSERT_TRUE(DoRunLoopWithTimeout());
550 EXPECT_FALSE(view1
->visible());
551 EXPECT_FALSE(view1
->IsDrawn());
553 EXPECT_FALSE(embedded_root
->visible());
554 EXPECT_FALSE(embedded_root
->IsDrawn());
556 // Make the node visible again.
558 VisibilityChangeObserver
observer(embedded_root
);
559 view1
->SetVisible(true);
560 ASSERT_TRUE(DoRunLoopWithTimeout());
563 EXPECT_TRUE(view1
->visible());
564 EXPECT_TRUE(view1
->IsDrawn());
566 EXPECT_TRUE(embedded_root
->visible());
567 EXPECT_TRUE(embedded_root
->IsDrawn());
572 class DrawnChangeObserver
: public ViewObserver
{
574 explicit DrawnChangeObserver(View
* view
) : view_(view
) {
575 view_
->AddObserver(this);
577 ~DrawnChangeObserver() override
{ view_
->RemoveObserver(this); }
580 // Overridden from ViewObserver:
581 void OnViewDrawnChanged(View
* view
) override
{
582 EXPECT_EQ(view
, view_
);
588 MOJO_DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver
);
593 TEST_F(ViewManagerTest
, Drawn
) {
594 View
* view1
= window_manager()->CreateView();
595 view1
->SetVisible(true);
596 window_manager()->GetRoot()->AddChild(view1
);
598 // Embed another app and verify initial state.
599 ViewManager
* embedded
= Embed(view1
);
600 ASSERT_NE(nullptr, embedded
);
601 ASSERT_NE(nullptr, embedded
->GetRoot());
602 View
* embedded_root
= embedded
->GetRoot();
603 EXPECT_TRUE(embedded_root
->visible());
604 EXPECT_TRUE(embedded_root
->IsDrawn());
606 // Change the visibility of the root, this should propagate a drawn state
607 // change to |embedded|.
609 DrawnChangeObserver
observer(embedded_root
);
610 window_manager()->GetRoot()->SetVisible(false);
611 ASSERT_TRUE(DoRunLoopWithTimeout());
614 EXPECT_TRUE(view1
->visible());
615 EXPECT_FALSE(view1
->IsDrawn());
617 EXPECT_TRUE(embedded_root
->visible());
618 EXPECT_FALSE(embedded_root
->IsDrawn());
621 // TODO(beng): tests for view event dispatcher.
622 // - verify that we see events for all views.
626 class FocusChangeObserver
: public ViewObserver
{
628 explicit FocusChangeObserver(View
* view
)
629 : view_(view
), last_gained_focus_(nullptr), last_lost_focus_(nullptr) {
630 view_
->AddObserver(this);
632 ~FocusChangeObserver() override
{ view_
->RemoveObserver(this); }
634 View
* last_gained_focus() { return last_gained_focus_
; }
636 View
* last_lost_focus() { return last_lost_focus_
; }
639 // Overridden from ViewObserver.
640 void OnViewFocusChanged(View
* gained_focus
, View
* lost_focus
) override
{
641 last_gained_focus_
= gained_focus
;
642 last_lost_focus_
= lost_focus
;
647 View
* last_gained_focus_
;
648 View
* last_lost_focus_
;
650 MOJO_DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver
);
655 TEST_F(ViewManagerTest
, Focus
) {
656 View
* view1
= window_manager()->CreateView();
657 view1
->SetVisible(true);
658 window_manager()->GetRoot()->AddChild(view1
);
660 ViewManager
* embedded
= Embed(view1
);
661 ASSERT_NE(nullptr, embedded
);
662 View
* view11
= embedded
->CreateView();
663 view11
->SetVisible(true);
664 embedded
->GetRoot()->AddChild(view11
);
666 // TODO(alhaad): Figure out why switching focus between views from different
667 // connections is causing the tests to crash and add tests for that.
669 View
* embedded_root
= embedded
->GetRoot();
670 FocusChangeObserver
observer(embedded_root
);
671 embedded_root
->SetFocus();
672 ASSERT_TRUE(DoRunLoopWithTimeout());
673 ASSERT_NE(nullptr, observer
.last_gained_focus());
674 EXPECT_EQ(embedded_root
->id(), observer
.last_gained_focus()->id());
677 FocusChangeObserver
observer(view11
);
679 ASSERT_TRUE(DoRunLoopWithTimeout());
680 ASSERT_NE(nullptr, observer
.last_gained_focus());
681 ASSERT_NE(nullptr, observer
.last_lost_focus());
682 EXPECT_EQ(view11
->id(), observer
.last_gained_focus()->id());
683 EXPECT_EQ(embedded
->GetRoot()->id(), observer
.last_lost_focus()->id());
689 class DestroyedChangedObserver
: public ViewObserver
{
691 DestroyedChangedObserver(View
* view
, bool* got_destroy
)
693 got_destroy_(got_destroy
) {
694 view_
->AddObserver(this);
696 ~DestroyedChangedObserver() override
{
698 view_
->RemoveObserver(this);
702 // Overridden from ViewObserver:
703 void OnViewDestroyed(View
* view
) override
{
704 EXPECT_EQ(view
, view_
);
705 view_
->RemoveObserver(this);
706 *got_destroy_
= true;
713 MOJO_DISALLOW_COPY_AND_ASSIGN(DestroyedChangedObserver
);
718 // Verifies deleting a ViewManager sends the right notifications.
719 TEST_F(ViewManagerTest
, DeleteViewManager
) {
720 View
* view
= window_manager()->CreateView();
721 ASSERT_NE(nullptr, view
);
722 view
->SetVisible(true);
723 window_manager()->GetRoot()->AddChild(view
);
724 ViewManager
* view_manager
= Embed(view
);
725 ASSERT_TRUE(view_manager
);
726 bool got_destroy
= false;
727 DestroyedChangedObserver
observer(view_manager
->GetRoot(), &got_destroy
);
729 EXPECT_TRUE(got_disconnect());
730 EXPECT_TRUE(got_destroy
);
733 // Verifies two Embed()s in the same view trigger deletion of the first
735 TEST_F(ViewManagerTest
, DisconnectTriggersDelete
) {
736 View
* view
= window_manager()->CreateView();
737 ASSERT_NE(nullptr, view
);
738 view
->SetVisible(true);
739 window_manager()->GetRoot()->AddChild(view
);
740 ViewManager
* view_manager
= Embed(view
);
741 EXPECT_NE(view_manager
, window_manager());
742 View
* embedded_view
= view_manager
->CreateView();
743 // Embed again, this should trigger disconnect and deletion of view_manager.
745 DestroyedChangedObserver
observer(embedded_view
, &got_destroy
);
746 EXPECT_FALSE(got_disconnect());
748 EXPECT_TRUE(got_disconnect());
751 class ViewRemovedFromParentObserver
: public ViewObserver
{
753 explicit ViewRemovedFromParentObserver(View
* view
)
754 : view_(view
), was_removed_(false) {
755 view_
->AddObserver(this);
757 ~ViewRemovedFromParentObserver() override
{ view_
->RemoveObserver(this); }
759 bool was_removed() const { return was_removed_
; }
762 // Overridden from ViewObserver:
763 void OnTreeChanged(const TreeChangeParams
& params
) override
{
764 if (params
.target
== view_
&& !params
.new_parent
)
771 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewRemovedFromParentObserver
);
774 TEST_F(ViewManagerTest
, EmbedRemovesChildren
) {
775 View
* view1
= window_manager()->CreateView();
776 View
* view2
= window_manager()->CreateView();
777 window_manager()->GetRoot()->AddChild(view1
);
778 view1
->AddChild(view2
);
780 ViewRemovedFromParentObserver
observer(view2
);
781 ConnectToApplicationAndEmbed(view1
);
782 EXPECT_TRUE(observer
.was_removed());
783 EXPECT_EQ(nullptr, view2
->parent());
784 EXPECT_TRUE(view1
->children().empty());
786 // Run the message loop so the Embed() call above completes. Without this
787 // we may end up reconnecting to the test and rerunning the test, which is
788 // problematic since the other services don't shut down.
789 ASSERT_TRUE(DoRunLoopWithTimeout());
792 TEST_F(ViewManagerTest
, OnWillEmbed
) {
793 window_manager()->SetEmbedRoot();
795 View
* view1
= window_manager()->CreateView();
796 window_manager()->GetRoot()->AddChild(view1
);
798 ViewManager
* view_manager
= EmbedAllowingReembed(view1
);
799 View
* view2
= view_manager
->CreateView();
800 view_manager
->GetRoot()->AddChild(view2
);
802 EmbedAllowingReembed(view2
);
803 EXPECT_EQ(1u, on_will_embed_count());
806 // Verifies Embed() doesn't succeed if OnWillEmbed() returns false.
807 TEST_F(ViewManagerTest
, OnWillEmbedFails
) {
808 window_manager()->SetEmbedRoot();
810 View
* view1
= window_manager()->CreateView();
811 window_manager()->GetRoot()->AddChild(view1
);
813 ViewManager
* view_manager
= Embed(view1
);
814 View
* view2
= view_manager
->CreateView();
815 view_manager
->GetRoot()->AddChild(view2
);
817 clear_on_will_embed_count();
818 set_on_will_embed_return_value(false);
819 mojo::URLRequestPtr
request(mojo::URLRequest::New());
820 request
->url
= application_impl()->url();
821 view2
->EmbedAllowingReembed(request
.Pass());
823 EXPECT_TRUE(DoRunLoopWithTimeout());
824 EXPECT_EQ(1u, on_will_embed_count());
826 // The run loop above quits when OnWillEmbed() returns, which means it's
827 // possible there is still an OnEmbed() message in flight. Sets the bounds of
828 // |view1| and wait for it to the change in |view_manager|, that way we know
829 // |view_manager| has processed all messages for it.
830 EXPECT_TRUE(IncrementWidthAndWaitForChange(view1
, view_manager
));
832 EXPECT_EQ(1u, on_will_embed_count());
835 // Verify an Embed() from an ancestor is not allowed.
836 TEST_F(ViewManagerTest
, ReembedFails
) {
837 window_manager()->SetEmbedRoot();
839 View
* view1
= window_manager()->CreateView();
840 window_manager()->GetRoot()->AddChild(view1
);
842 ViewManager
* view_manager
= Embed(view1
);
843 ASSERT_TRUE(view_manager
);
844 View
* view2
= view_manager
->CreateView();
845 view_manager
->GetRoot()->AddChild(view2
);
848 // Try to embed in view2 from the window_manager. This should fail as the
849 // Embed() didn't grab reembed.
850 View
* view2_in_wm
= window_manager()->GetViewById(view2
->id());
851 ConnectToApplicationAndEmbed(view2_in_wm
);
853 // The Embed() call above returns immediately. To ensure the server has
854 // processed it nudge the bounds and wait for it to be processed.
855 EXPECT_TRUE(IncrementWidthAndWaitForChange(view1
, view_manager
));
857 EXPECT_EQ(nullptr, most_recent_view_manager());
860 // Verify an Embed() from an ancestor is allowed if the ancestor is an embed
861 // root and Embed was done by way of EmbedAllowingReembed().
862 TEST_F(ViewManagerTest
, ReembedSucceeds
) {
863 window_manager()->SetEmbedRoot();
865 View
* view1
= window_manager()->CreateView();
866 window_manager()->GetRoot()->AddChild(view1
);
868 ViewManager
* view_manager
= Embed(view1
);
869 View
* view2
= view_manager
->CreateView();
870 view_manager
->GetRoot()->AddChild(view2
);
871 EmbedAllowingReembed(view2
);
873 View
* view2_in_wm
= window_manager()->GetViewById(view2
->id());
874 ViewManager
* view_manager2
= Embed(view2_in_wm
);
875 ASSERT_TRUE(view_manager2
);
877 // The Embed() call above returns immediately. To ensure the server has
878 // processed it nudge the bounds and wait for it to be processed.
879 EXPECT_TRUE(IncrementWidthAndWaitForChange(view1
, view_manager
));
881 EXPECT_EQ(nullptr, most_recent_view_manager());