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/message_loop/message_loop.h"
7 #include "base/run_loop.h"
8 #include "base/strings/stringprintf.h"
9 #include "components/mus/ids.h"
10 #include "components/mus/public/interfaces/view_tree.mojom.h"
11 #include "components/mus/public/interfaces/view_tree_host.mojom.h"
12 #include "components/mus/test_change_tracker.h"
13 #include "mojo/application/public/cpp/application_delegate.h"
14 #include "mojo/application/public/cpp/application_impl.h"
15 #include "mojo/application/public/cpp/application_test_base.h"
17 using mojo::ApplicationConnection
;
18 using mojo::ApplicationDelegate
;
21 using mojo::ConnectionSpecificId
;
22 using mojo::ERROR_CODE_NONE
;
23 using mojo::ErrorCode
;
26 using mojo::InterfaceRequest
;
27 using mojo::ORDER_DIRECTION_ABOVE
;
28 using mojo::ORDER_DIRECTION_BELOW
;
29 using mojo::OrderDirection
;
31 using mojo::ServiceProvider
;
32 using mojo::ServiceProviderPtr
;
34 using mojo::ViewDataPtr
;
36 using mojo::ViewTreeClient
;
37 using mojo::ViewportMetricsPtr
;
39 namespace view_manager
{
41 // Creates an id used for transport from the specified parameters.
42 Id
BuildViewId(ConnectionSpecificId connection_id
,
43 ConnectionSpecificId view_id
) {
44 return (connection_id
<< 16) | view_id
;
47 // Callback function from ViewTree functions. ----------------------------------
49 void BoolResultCallback(base::RunLoop
* run_loop
,
52 *result_cache
= result
;
56 void ErrorCodeResultCallback(base::RunLoop
* run_loop
,
57 ErrorCode
* result_cache
,
59 *result_cache
= result
;
63 void ViewTreeResultCallback(base::RunLoop
* run_loop
,
64 std::vector
<TestView
>* views
,
65 Array
<ViewDataPtr
> results
) {
66 ViewDatasToTestViews(results
, views
);
70 void EmbedCallbackImpl(base::RunLoop
* run_loop
,
73 ConnectionSpecificId connection_id
) {
74 *result_cache
= result
;
78 // -----------------------------------------------------------------------------
80 bool EmbedUrl(mojo::ApplicationImpl
* app
,
85 base::RunLoop run_loop
;
87 mojo::URLRequestPtr
request(mojo::URLRequest::New());
88 request
->url
= mojo::String::From(url
);
89 scoped_ptr
<ApplicationConnection
> connection
=
90 app
->ConnectToApplication(request
.Pass());
91 mojo::ViewTreeClientPtr client
;
92 connection
->ConnectToService(&client
);
93 vm
->Embed(root_id
, client
.Pass(), mojo::ViewTree::ACCESS_POLICY_DEFAULT
,
94 base::Bind(&EmbedCallbackImpl
, &run_loop
, &result
));
100 bool Embed(ViewTree
* vm
, Id root_id
, mojo::ViewTreeClientPtr client
) {
102 base::RunLoop run_loop
;
104 vm
->Embed(root_id
, client
.Pass(), mojo::ViewTree::ACCESS_POLICY_DEFAULT
,
105 base::Bind(&EmbedCallbackImpl
, &run_loop
, &result
));
111 ErrorCode
CreateViewWithErrorCode(ViewTree
* vm
, Id view_id
) {
112 ErrorCode result
= ERROR_CODE_NONE
;
113 base::RunLoop run_loop
;
114 vm
->CreateView(view_id
,
115 base::Bind(&ErrorCodeResultCallback
, &run_loop
, &result
));
120 bool AddView(ViewTree
* vm
, Id parent
, Id child
) {
122 base::RunLoop run_loop
;
123 vm
->AddView(parent
, child
,
124 base::Bind(&BoolResultCallback
, &run_loop
, &result
));
129 bool RemoveViewFromParent(ViewTree
* vm
, Id view_id
) {
131 base::RunLoop run_loop
;
132 vm
->RemoveViewFromParent(view_id
,
133 base::Bind(&BoolResultCallback
, &run_loop
, &result
));
138 bool ReorderView(ViewTree
* vm
,
141 OrderDirection direction
) {
143 base::RunLoop run_loop
;
144 vm
->ReorderView(view_id
, relative_view_id
, direction
,
145 base::Bind(&BoolResultCallback
, &run_loop
, &result
));
150 void GetViewTree(ViewTree
* vm
, Id view_id
, std::vector
<TestView
>* views
) {
151 base::RunLoop run_loop
;
152 vm
->GetViewTree(view_id
,
153 base::Bind(&ViewTreeResultCallback
, &run_loop
, views
));
157 bool DeleteView(ViewTree
* vm
, Id view_id
) {
158 base::RunLoop run_loop
;
160 vm
->DeleteView(view_id
, base::Bind(&BoolResultCallback
, &run_loop
, &result
));
165 bool SetViewBounds(ViewTree
* vm
, Id view_id
, int x
, int y
, int w
, int h
) {
166 base::RunLoop run_loop
;
168 RectPtr
rect(mojo::Rect::New());
173 vm
->SetViewBounds(view_id
, rect
.Pass(),
174 base::Bind(&BoolResultCallback
, &run_loop
, &result
));
179 bool SetViewVisibility(ViewTree
* vm
, Id view_id
, bool visible
) {
180 base::RunLoop run_loop
;
182 vm
->SetViewVisibility(view_id
, visible
,
183 base::Bind(&BoolResultCallback
, &run_loop
, &result
));
188 bool SetViewProperty(ViewTree
* vm
,
190 const std::string
& name
,
191 const std::vector
<uint8_t>* data
) {
192 base::RunLoop run_loop
;
194 Array
<uint8_t> mojo_data
;
196 mojo_data
= Array
<uint8_t>::From(*data
);
197 vm
->SetViewProperty(view_id
, name
, mojo_data
.Pass(),
198 base::Bind(&BoolResultCallback
, &run_loop
, &result
));
203 // Utility functions -----------------------------------------------------------
205 // Waits for all messages to be received by |vm|. This is done by attempting to
206 // create a bogus view. When we get the response we know all messages have been
208 bool WaitForAllMessages(ViewTree
* vm
) {
209 ErrorCode result
= ERROR_CODE_NONE
;
210 base::RunLoop run_loop
;
211 vm
->CreateView(ViewIdToTransportId(InvalidViewId()),
212 base::Bind(&ErrorCodeResultCallback
, &run_loop
, &result
));
214 return result
!= ERROR_CODE_NONE
;
217 const Id kNullParentId
= 0;
218 std::string
IdToString(Id id
) {
219 return (id
== kNullParentId
)
221 : base::StringPrintf("%d,%d", mojo::HiWord(id
), mojo::LoWord(id
));
224 std::string
ViewParentToString(Id view
, Id parent
) {
225 return base::StringPrintf("view=%s parent=%s", IdToString(view
).c_str(),
226 IdToString(parent
).c_str());
229 // -----------------------------------------------------------------------------
231 // A ViewTreeClient implementation that logs all changes to a tracker.
232 class ViewTreeClientImpl
: public mojo::ViewTreeClient
,
233 public TestChangeTracker::Delegate
{
235 explicit ViewTreeClientImpl(mojo::ApplicationImpl
* app
)
236 : binding_(this), app_(app
), connection_id_(0), root_view_id_(0) {
237 tracker_
.set_delegate(this);
240 void Bind(mojo::InterfaceRequest
<mojo::ViewTreeClient
> request
) {
241 binding_
.Bind(request
.Pass());
244 mojo::ViewTree
* tree() { return tree_
.get(); }
245 TestChangeTracker
* tracker() { return &tracker_
; }
247 // Runs a nested MessageLoop until |count| changes (calls to
248 // ViewTreeClient functions) have been received.
249 void WaitForChangeCount(size_t count
) {
250 if (count
== tracker_
.changes()->size())
253 ASSERT_TRUE(wait_state_
.get() == nullptr);
254 wait_state_
.reset(new WaitState
);
255 wait_state_
->change_count
= count
;
256 wait_state_
->run_loop
.Run();
260 // Runs a nested MessageLoop until OnEmbed() has been encountered.
261 void WaitForOnEmbed() {
264 embed_run_loop_
.reset(new base::RunLoop
);
265 embed_run_loop_
->Run();
266 embed_run_loop_
.reset();
269 bool WaitForIncomingMethodCall() {
270 return binding_
.WaitForIncomingMethodCall();
273 Id
CreateView(ConnectionSpecificId view_id
) {
274 ErrorCode result
= ERROR_CODE_NONE
;
275 base::RunLoop run_loop
;
276 Id id
= BuildViewId(connection_id_
, view_id
);
278 id
, base::Bind(&ErrorCodeResultCallback
, &run_loop
, &result
));
280 return result
== ERROR_CODE_NONE
? id
: 0;
283 void set_root_view(Id root_view_id
) { root_view_id_
= root_view_id
; }
286 // Used when running a nested MessageLoop.
288 WaitState() : change_count(0) {}
290 // Number of changes waiting for.
292 base::RunLoop run_loop
;
295 // TestChangeTracker::Delegate:
296 void OnChangeAdded() override
{
297 if (wait_state_
.get() &&
298 wait_state_
->change_count
== tracker_
.changes()->size()) {
299 wait_state_
->run_loop
.Quit();
304 void OnEmbed(ConnectionSpecificId connection_id
,
306 mojo::ViewTreePtr tree
,
307 mojo::Id focused_view_id
,
308 uint32_t access_policy
) override
{
309 // TODO(sky): add coverage of |focused_view_id|.
311 connection_id_
= connection_id
;
312 tracker()->OnEmbed(connection_id
, root
.Pass());
314 embed_run_loop_
->Quit();
316 void OnEmbeddedAppDisconnected(Id view_id
) override
{
317 tracker()->OnEmbeddedAppDisconnected(view_id
);
319 void OnUnembed() override
{ tracker()->OnUnembed(); }
320 void OnViewBoundsChanged(Id view_id
,
322 RectPtr new_bounds
) override
{
323 // The bounds of the root may change during startup on Android at random
324 // times. As this doesn't matter, and shouldn't impact test exepctations,
326 if (view_id
== root_view_id_
)
328 tracker()->OnViewBoundsChanged(view_id
, old_bounds
.Pass(),
331 void OnViewViewportMetricsChanged(ViewportMetricsPtr old_metrics
,
332 ViewportMetricsPtr new_metrics
) override
{
333 // Don't track the metrics as they are available at an indeterministic time
336 void OnViewHierarchyChanged(Id view
,
339 Array
<ViewDataPtr
> views
) override
{
340 tracker()->OnViewHierarchyChanged(view
, new_parent
, old_parent
,
343 void OnViewReordered(Id view_id
,
345 OrderDirection direction
) override
{
346 tracker()->OnViewReordered(view_id
, relative_view_id
, direction
);
348 void OnViewDeleted(Id view
) override
{ tracker()->OnViewDeleted(view
); }
349 void OnViewVisibilityChanged(uint32_t view
, bool visible
) override
{
350 tracker()->OnViewVisibilityChanged(view
, visible
);
352 void OnViewDrawnStateChanged(uint32_t view
, bool drawn
) override
{
353 tracker()->OnViewDrawnStateChanged(view
, drawn
);
355 void OnViewInputEvent(Id view_id
,
357 const Callback
<void()>& callback
) override
{
358 tracker()->OnViewInputEvent(view_id
, event
.Pass());
361 void OnViewSharedPropertyChanged(uint32_t view
,
363 Array
<uint8_t> new_data
) override
{
364 tracker_
.OnViewSharedPropertyChanged(view
, name
, new_data
.Pass());
366 // TODO(sky): add testing coverage.
367 void OnViewFocused(uint32_t focused_view_id
) override
{}
369 TestChangeTracker tracker_
;
371 mojo::ViewTreePtr tree_
;
373 // If non-null we're waiting for OnEmbed() using this RunLoop.
374 scoped_ptr
<base::RunLoop
> embed_run_loop_
;
376 // If non-null we're waiting for a certain number of change notifications to
378 scoped_ptr
<WaitState
> wait_state_
;
380 mojo::Binding
<ViewTreeClient
> binding_
;
381 mojo::ApplicationImpl
* app_
;
385 DISALLOW_COPY_AND_ASSIGN(ViewTreeClientImpl
);
388 // -----------------------------------------------------------------------------
390 // InterfaceFactory for vending ViewTreeClientImpls.
391 class ViewTreeClientFactory
: public mojo::InterfaceFactory
<ViewTreeClient
> {
393 explicit ViewTreeClientFactory(mojo::ApplicationImpl
* app
) : app_(app
) {}
394 ~ViewTreeClientFactory() override
{}
396 // Runs a nested MessageLoop until a new instance has been created.
397 scoped_ptr
<ViewTreeClientImpl
> WaitForInstance() {
398 if (!client_impl_
.get()) {
399 DCHECK(!run_loop_
.get());
400 run_loop_
.reset(new base::RunLoop
);
404 return client_impl_
.Pass();
408 // InterfaceFactory<ViewTreeClient>:
409 void Create(ApplicationConnection
* connection
,
410 InterfaceRequest
<ViewTreeClient
> request
) override
{
411 client_impl_
.reset(new ViewTreeClientImpl(app_
));
412 client_impl_
->Bind(request
.Pass());
417 mojo::ApplicationImpl
* app_
;
418 scoped_ptr
<ViewTreeClientImpl
> client_impl_
;
419 scoped_ptr
<base::RunLoop
> run_loop_
;
421 DISALLOW_COPY_AND_ASSIGN(ViewTreeClientFactory
);
424 class ViewTreeAppTest
: public mojo::test::ApplicationTestBase
,
425 public ApplicationDelegate
{
428 : connection_id_1_(0), connection_id_2_(0), root_view_id_(0) {}
429 ~ViewTreeAppTest() override
{}
432 // Returns the changes from the various connections.
433 std::vector
<Change
>* changes1() { return vm_client1_
->tracker()->changes(); }
434 std::vector
<Change
>* changes2() { return vm_client2_
->tracker()->changes(); }
435 std::vector
<Change
>* changes3() { return vm_client3_
->tracker()->changes(); }
437 // Various connections. |vm1()|, being the first connection, has special
438 // permissions (it's treated as the window manager).
439 ViewTree
* vm1() { return vm_client1_
->tree(); }
440 ViewTree
* vm2() { return vm_client2_
->tree(); }
441 ViewTree
* vm3() { return vm_client3_
->tree(); }
443 ViewTreeClientImpl
* vm_client1() { return vm_client1_
.get(); }
444 ViewTreeClientImpl
* vm_client2() { return vm_client2_
.get(); }
445 ViewTreeClientImpl
* vm_client3() { return vm_client3_
.get(); }
447 Id
root_view_id() const { return root_view_id_
; }
449 int connection_id_1() const { return connection_id_1_
; }
450 int connection_id_2() const { return connection_id_2_
; }
452 void EstablishSecondConnectionWithRoot(Id root_id
) {
453 ASSERT_TRUE(vm_client2_
.get() == nullptr);
455 EstablishConnectionViaEmbed(vm1(), root_id
, &connection_id_2_
);
456 ASSERT_GT(connection_id_2_
, 0);
457 ASSERT_TRUE(vm_client2_
.get() != nullptr);
458 vm_client2_
->set_root_view(root_view_id_
);
461 void EstablishSecondConnection(bool create_initial_view
) {
463 if (create_initial_view
) {
464 view_1_1
= vm_client1()->CreateView(1);
465 ASSERT_TRUE(view_1_1
);
467 ASSERT_NO_FATAL_FAILURE(
468 EstablishSecondConnectionWithRoot(BuildViewId(connection_id_1(), 1)));
470 if (create_initial_view
) {
471 EXPECT_EQ("[" + ViewParentToString(view_1_1
, kNullParentId
) + "]",
472 ChangeViewDescription(*changes2()));
476 void EstablishThirdConnection(ViewTree
* owner
, Id root_id
) {
477 ASSERT_TRUE(vm_client3_
.get() == nullptr);
478 vm_client3_
= EstablishConnectionViaEmbed(owner
, root_id
, nullptr);
479 ASSERT_TRUE(vm_client3_
.get() != nullptr);
480 vm_client3_
->set_root_view(root_view_id_
);
483 scoped_ptr
<ViewTreeClientImpl
> WaitForViewTreeClient() {
484 return client_factory_
->WaitForInstance();
487 // Establishes a new connection by way of Embed() on the specified
489 scoped_ptr
<ViewTreeClientImpl
>
490 EstablishConnectionViaEmbed(ViewTree
* owner
, Id root_id
, int* connection_id
) {
491 return EstablishConnectionViaEmbedWithPolicyBitmask(
492 owner
, root_id
, mojo::ViewTree::ACCESS_POLICY_DEFAULT
, connection_id
);
495 scoped_ptr
<ViewTreeClientImpl
> EstablishConnectionViaEmbedWithPolicyBitmask(
498 uint32_t policy_bitmask
,
499 int* connection_id
) {
500 if (!EmbedUrl(application_impl(), owner
, application_impl()->url(),
502 ADD_FAILURE() << "Embed() failed";
505 scoped_ptr
<ViewTreeClientImpl
> client
= client_factory_
->WaitForInstance();
507 ADD_FAILURE() << "WaitForInstance failed";
510 client
->WaitForOnEmbed();
513 SingleChangeToDescription(*client
->tracker()->changes()));
515 *connection_id
= (*client
->tracker()->changes())[0].connection_id
;
516 return client
.Pass();
519 // ApplicationTestBase:
520 ApplicationDelegate
* GetApplicationDelegate() override
{ return this; }
521 void SetUp() override
{
522 ApplicationTestBase::SetUp();
523 client_factory_
.reset(new ViewTreeClientFactory(application_impl()));
524 mojo::URLRequestPtr
request(mojo::URLRequest::New());
525 request
->url
= mojo::String::From("mojo:mus");
527 mojo::ViewTreeHostFactoryPtr factory
;
528 application_impl()->ConnectToService(request
.Pass(), &factory
);
530 mojo::ViewTreeClientPtr tree_client_ptr
;
531 vm_client1_
.reset(new ViewTreeClientImpl(application_impl()));
532 vm_client1_
->Bind(GetProxy(&tree_client_ptr
));
534 factory
->CreateViewTreeHost(GetProxy(&host_
), mojo::ViewTreeHostClientPtr(),
535 tree_client_ptr
.Pass());
537 // Next we should get an embed call on the "window manager" client.
538 vm_client1_
->WaitForIncomingMethodCall();
540 ASSERT_EQ(1u, changes1()->size());
541 EXPECT_EQ(CHANGE_TYPE_EMBED
, (*changes1())[0].type
);
542 // All these tests assume 1 for the client id. The only real assertion here
543 // is the client id is not zero, but adding this as rest of code here
545 ASSERT_GT((*changes1())[0].connection_id
, 0);
546 connection_id_1_
= (*changes1())[0].connection_id
;
547 ASSERT_FALSE((*changes1())[0].views
.empty());
548 root_view_id_
= (*changes1())[0].views
[0].view_id
;
549 vm_client1_
->set_root_view(root_view_id_
);
553 // ApplicationDelegate implementation.
554 bool ConfigureIncomingConnection(ApplicationConnection
* connection
) override
{
555 connection
->AddService(client_factory_
.get());
559 scoped_ptr
<ViewTreeClientImpl
> vm_client1_
;
560 scoped_ptr
<ViewTreeClientImpl
> vm_client2_
;
561 scoped_ptr
<ViewTreeClientImpl
> vm_client3_
;
563 mojo::ViewTreeHostPtr host_
;
566 scoped_ptr
<ViewTreeClientFactory
> client_factory_
;
567 int connection_id_1_
;
568 int connection_id_2_
;
571 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTreeAppTest
);
574 // Verifies two clients/connections get different ids.
575 TEST_F(ViewTreeAppTest
, TwoClientsGetDifferentConnectionIds
) {
576 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
578 ASSERT_EQ(1u, changes2()->size());
579 ASSERT_NE(connection_id_1(), connection_id_2());
582 // Verifies when Embed() is invoked any child views are removed.
583 TEST_F(ViewTreeAppTest
, ViewsRemovedWhenEmbedding
) {
584 // Two views 1 and 2. 2 is parented to 1.
585 Id view_1_1
= vm_client1()->CreateView(1);
586 ASSERT_TRUE(view_1_1
);
587 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
589 Id view_1_2
= vm_client1()->CreateView(2);
590 ASSERT_TRUE(view_1_2
);
591 ASSERT_TRUE(AddView(vm1(), view_1_1
, view_1_2
));
593 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
594 ASSERT_EQ(1u, changes2()->size());
595 ASSERT_EQ(1u, (*changes2())[0].views
.size());
596 EXPECT_EQ("[" + ViewParentToString(view_1_1
, kNullParentId
) + "]",
597 ChangeViewDescription(*changes2()));
599 // Embed() removed view 2.
601 std::vector
<TestView
> views
;
602 GetViewTree(vm1(), view_1_2
, &views
);
603 EXPECT_EQ(ViewParentToString(view_1_2
, kNullParentId
),
604 SingleViewDescription(views
));
607 // vm2 should not see view 2.
609 std::vector
<TestView
> views
;
610 GetViewTree(vm2(), view_1_1
, &views
);
611 EXPECT_EQ(ViewParentToString(view_1_1
, kNullParentId
),
612 SingleViewDescription(views
));
615 std::vector
<TestView
> views
;
616 GetViewTree(vm2(), view_1_2
, &views
);
617 EXPECT_TRUE(views
.empty());
620 // Views 3 and 4 in connection 2.
621 Id view_2_3
= vm_client2()->CreateView(3);
622 Id view_2_4
= vm_client2()->CreateView(4);
623 ASSERT_TRUE(view_2_3
);
624 ASSERT_TRUE(view_2_4
);
625 ASSERT_TRUE(AddView(vm2(), view_2_3
, view_2_4
));
627 // Connection 3 rooted at 2.
628 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3
));
630 // View 4 should no longer have a parent.
632 std::vector
<TestView
> views
;
633 GetViewTree(vm2(), view_2_3
, &views
);
634 EXPECT_EQ(ViewParentToString(view_2_3
, kNullParentId
),
635 SingleViewDescription(views
));
638 GetViewTree(vm2(), view_2_4
, &views
);
639 EXPECT_EQ(ViewParentToString(view_2_4
, kNullParentId
),
640 SingleViewDescription(views
));
643 // And view 4 should not be visible to connection 3.
645 std::vector
<TestView
> views
;
646 GetViewTree(vm3(), view_2_3
, &views
);
647 EXPECT_EQ(ViewParentToString(view_2_3
, kNullParentId
),
648 SingleViewDescription(views
));
652 // Verifies once Embed() has been invoked the parent connection can't see any
654 TEST_F(ViewTreeAppTest
, CantAccessChildrenOfEmbeddedView
) {
655 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
657 Id view_1_1
= BuildViewId(connection_id_1(), 1);
658 Id view_2_2
= vm_client2()->CreateView(2);
659 ASSERT_TRUE(view_2_2
);
660 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
662 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2
));
664 Id view_3_3
= vm_client3()->CreateView(3);
665 ASSERT_TRUE(view_3_3
);
666 ASSERT_TRUE(AddView(vm3(), view_2_2
, view_3_3
));
668 // Even though 3 is a child of 2 connection 2 can't see 3 as it's from a
669 // different connection.
671 std::vector
<TestView
> views
;
672 GetViewTree(vm2(), view_2_2
, &views
);
673 EXPECT_EQ(ViewParentToString(view_2_2
, view_1_1
),
674 SingleViewDescription(views
));
677 // Connection 2 shouldn't be able to get view 3 at all.
679 std::vector
<TestView
> views
;
680 GetViewTree(vm2(), view_3_3
, &views
);
681 EXPECT_TRUE(views
.empty());
684 // Connection 1 should be able to see it all (its the root).
686 std::vector
<TestView
> views
;
687 GetViewTree(vm1(), view_1_1
, &views
);
688 ASSERT_EQ(3u, views
.size());
689 EXPECT_EQ(ViewParentToString(view_1_1
, kNullParentId
), views
[0].ToString());
690 EXPECT_EQ(ViewParentToString(view_2_2
, view_1_1
), views
[1].ToString());
691 EXPECT_EQ(ViewParentToString(view_3_3
, view_2_2
), views
[2].ToString());
695 // Verifies once Embed() has been invoked the parent can't mutate the children.
696 TEST_F(ViewTreeAppTest
, CantModifyChildrenOfEmbeddedView
) {
697 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
699 Id view_1_1
= BuildViewId(connection_id_1(), 1);
700 Id view_2_2
= vm_client2()->CreateView(2);
701 ASSERT_TRUE(view_2_2
);
702 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
704 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2
));
706 Id view_2_3
= vm_client2()->CreateView(3);
707 ASSERT_TRUE(view_2_3
);
708 // Connection 2 shouldn't be able to add anything to the view anymore.
709 ASSERT_FALSE(AddView(vm2(), view_2_2
, view_2_3
));
711 // Create view 3 in connection 3 and add it to view 3.
712 Id view_3_3
= vm_client3()->CreateView(3);
713 ASSERT_TRUE(view_3_3
);
714 ASSERT_TRUE(AddView(vm3(), view_2_2
, view_3_3
));
716 // Connection 2 shouldn't be able to remove view 3.
717 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_3_3
));
720 // Verifies client gets a valid id.
721 TEST_F(ViewTreeAppTest
, CreateView
) {
722 Id view_1_1
= vm_client1()->CreateView(1);
723 ASSERT_TRUE(view_1_1
);
724 EXPECT_TRUE(changes1()->empty());
726 // Can't create a view with the same id.
727 ASSERT_EQ(mojo::ERROR_CODE_VALUE_IN_USE
,
728 CreateViewWithErrorCode(vm1(), view_1_1
));
729 EXPECT_TRUE(changes1()->empty());
731 // Can't create a view with a bogus connection id.
733 mojo::ERROR_CODE_ILLEGAL_ARGUMENT
,
734 CreateViewWithErrorCode(vm1(), BuildViewId(connection_id_1() + 1, 1)));
735 EXPECT_TRUE(changes1()->empty());
738 // Verifies AddView fails when view is already in position.
739 TEST_F(ViewTreeAppTest
, AddViewWithNoChange
) {
740 Id view_1_2
= vm_client1()->CreateView(2);
741 Id view_1_3
= vm_client1()->CreateView(3);
742 ASSERT_TRUE(view_1_2
);
743 ASSERT_TRUE(view_1_3
);
745 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
747 // Make 3 a child of 2.
748 ASSERT_TRUE(AddView(vm1(), view_1_2
, view_1_3
));
750 // Try again, this should fail.
751 EXPECT_FALSE(AddView(vm1(), view_1_2
, view_1_3
));
754 // Verifies AddView fails when view is already in position.
755 TEST_F(ViewTreeAppTest
, AddAncestorFails
) {
756 Id view_1_2
= vm_client1()->CreateView(2);
757 Id view_1_3
= vm_client1()->CreateView(3);
758 ASSERT_TRUE(view_1_2
);
759 ASSERT_TRUE(view_1_3
);
761 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
763 // Make 3 a child of 2.
764 ASSERT_TRUE(AddView(vm1(), view_1_2
, view_1_3
));
766 // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3.
767 EXPECT_FALSE(AddView(vm1(), view_1_3
, view_1_2
));
770 // Verifies adding to root sends right notifications.
771 TEST_F(ViewTreeAppTest
, AddToRoot
) {
772 Id view_1_21
= vm_client1()->CreateView(21);
773 Id view_1_3
= vm_client1()->CreateView(3);
774 ASSERT_TRUE(view_1_21
);
775 ASSERT_TRUE(view_1_3
);
777 Id view_1_1
= BuildViewId(connection_id_1(), 1);
778 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
781 // Make 3 a child of 21.
782 ASSERT_TRUE(AddView(vm1(), view_1_21
, view_1_3
));
784 // Make 21 a child of 1.
785 ASSERT_TRUE(AddView(vm1(), view_1_1
, view_1_21
));
787 // Connection 2 should not be told anything (because the view is from a
788 // different connection). Create a view to ensure we got a response from
790 ASSERT_TRUE(vm_client2()->CreateView(100));
791 EXPECT_TRUE(changes2()->empty());
794 // Verifies HierarchyChanged is correctly sent for various adds/removes.
795 TEST_F(ViewTreeAppTest
, ViewHierarchyChangedViews
) {
797 Id view_1_2
= vm_client1()->CreateView(2);
798 ASSERT_TRUE(view_1_2
);
799 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2
, true));
800 Id view_1_11
= vm_client1()->CreateView(11);
801 ASSERT_TRUE(view_1_11
);
802 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_11
, true));
803 ASSERT_TRUE(AddView(vm1(), view_1_2
, view_1_11
));
805 Id view_1_1
= BuildViewId(connection_id_1(), 1);
806 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
807 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1
, true));
809 ASSERT_TRUE(WaitForAllMessages(vm2()));
814 // Client 2 should not get anything (1,2 is from another connection).
815 ASSERT_TRUE(AddView(vm1(), view_1_1
, view_1_2
));
816 ASSERT_TRUE(WaitForAllMessages(vm2()));
817 EXPECT_TRUE(changes2()->empty());
820 // 0,1->1,1->1,2->1,11.
822 // Client 2 is now connected to the root, so it should have gotten a drawn
824 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
825 vm_client2_
->WaitForChangeCount(1u);
826 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1
) + " drawn=true",
827 SingleChangeToDescription(*changes2()));
832 // Client 2 is no longer connected to the root, should get drawn state
835 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1
));
836 vm_client2_
->WaitForChangeCount(1);
837 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1
) + " drawn=false",
838 SingleChangeToDescription(*changes2()));
841 // 1,1->1,2->1,11->1,111.
842 Id view_1_111
= vm_client1()->CreateView(111);
843 ASSERT_TRUE(view_1_111
);
844 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_111
, true));
847 ASSERT_TRUE(AddView(vm1(), view_1_11
, view_1_111
));
848 ASSERT_TRUE(WaitForAllMessages(vm2()));
849 EXPECT_TRUE(changes2()->empty());
852 // 0,1->1,1->1,2->1,11->1,111
855 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
856 vm_client2_
->WaitForChangeCount(1);
857 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1
) + " drawn=true",
858 SingleChangeToDescription(*changes2()));
862 TEST_F(ViewTreeAppTest
, ViewHierarchyChangedAddingKnownToUnknown
) {
863 // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
865 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
866 Id view_1_1
= BuildViewId(connection_id_1(), 1);
868 Id view_2_11
= vm_client2()->CreateView(11);
869 Id view_2_2
= vm_client2()->CreateView(2);
870 Id view_2_21
= vm_client2()->CreateView(21);
871 ASSERT_TRUE(view_2_11
);
872 ASSERT_TRUE(view_2_2
);
873 ASSERT_TRUE(view_2_21
);
875 // Set up the hierarchy.
876 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
877 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_11
));
878 ASSERT_TRUE(AddView(vm2(), view_2_2
, view_2_21
));
880 // Remove 11, should result in a hierarchy change for the root.
883 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11
));
885 vm_client1_
->WaitForChangeCount(1);
886 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_11
) +
887 " new_parent=null old_parent=" + IdToString(view_1_1
),
888 SingleChangeToDescription(*changes1()));
894 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
895 vm_client1_
->WaitForChangeCount(1);
896 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2
) + " new_parent=" +
897 IdToString(view_1_1
) + " old_parent=null",
898 SingleChangeToDescription(*changes1()));
899 EXPECT_EQ("[" + ViewParentToString(view_2_2
, view_1_1
) + "],[" +
900 ViewParentToString(view_2_21
, view_2_2
) + "]",
901 ChangeViewDescription(*changes1()));
905 TEST_F(ViewTreeAppTest
, ReorderView
) {
906 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
908 Id view_2_1
= vm_client2()->CreateView(1);
909 Id view_2_2
= vm_client2()->CreateView(2);
910 Id view_2_3
= vm_client2()->CreateView(3);
911 Id view_1_4
= vm_client1()->CreateView(4); // Peer to 1,1
912 Id view_1_5
= vm_client1()->CreateView(5); // Peer to 1,1
913 Id view_2_6
= vm_client2()->CreateView(6); // Child of 1,2.
914 Id view_2_7
= vm_client2()->CreateView(7); // Unparented.
915 Id view_2_8
= vm_client2()->CreateView(8); // Unparented.
916 ASSERT_TRUE(view_2_1
);
917 ASSERT_TRUE(view_2_2
);
918 ASSERT_TRUE(view_2_3
);
919 ASSERT_TRUE(view_1_4
);
920 ASSERT_TRUE(view_1_5
);
921 ASSERT_TRUE(view_2_6
);
922 ASSERT_TRUE(view_2_7
);
923 ASSERT_TRUE(view_2_8
);
925 ASSERT_TRUE(AddView(vm2(), view_2_1
, view_2_2
));
926 ASSERT_TRUE(AddView(vm2(), view_2_2
, view_2_6
));
927 ASSERT_TRUE(AddView(vm2(), view_2_1
, view_2_3
));
928 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_4
));
929 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_5
));
930 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_2_1
));
934 ASSERT_TRUE(ReorderView(vm2(), view_2_2
, view_2_3
, ORDER_DIRECTION_ABOVE
));
936 vm_client1_
->WaitForChangeCount(1);
937 EXPECT_EQ("Reordered view=" + IdToString(view_2_2
) + " relative=" +
938 IdToString(view_2_3
) + " direction=above",
939 SingleChangeToDescription(*changes1()));
944 ASSERT_TRUE(ReorderView(vm2(), view_2_2
, view_2_3
, ORDER_DIRECTION_BELOW
));
946 vm_client1_
->WaitForChangeCount(1);
947 EXPECT_EQ("Reordered view=" + IdToString(view_2_2
) + " relative=" +
948 IdToString(view_2_3
) + " direction=below",
949 SingleChangeToDescription(*changes1()));
952 // view2 is already below view3.
953 EXPECT_FALSE(ReorderView(vm2(), view_2_2
, view_2_3
, ORDER_DIRECTION_BELOW
));
955 // view4 & 5 are unknown to connection2_.
956 EXPECT_FALSE(ReorderView(vm2(), view_1_4
, view_1_5
, ORDER_DIRECTION_ABOVE
));
958 // view6 & view3 have different parents.
959 EXPECT_FALSE(ReorderView(vm1(), view_2_3
, view_2_6
, ORDER_DIRECTION_ABOVE
));
961 // Non-existent view-ids
962 EXPECT_FALSE(ReorderView(vm1(), BuildViewId(connection_id_1(), 27),
963 BuildViewId(connection_id_1(), 28),
964 ORDER_DIRECTION_ABOVE
));
966 // view7 & view8 are un-parented.
967 EXPECT_FALSE(ReorderView(vm1(), view_2_7
, view_2_8
, ORDER_DIRECTION_ABOVE
));
970 // Verifies DeleteView works.
971 TEST_F(ViewTreeAppTest
, DeleteView
) {
972 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
973 Id view_1_1
= BuildViewId(connection_id_1(), 1);
974 Id view_2_2
= vm_client2()->CreateView(2);
975 ASSERT_TRUE(view_2_2
);
977 // Make 2 a child of 1.
980 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
981 vm_client1_
->WaitForChangeCount(1);
982 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2
) + " new_parent=" +
983 IdToString(view_1_1
) + " old_parent=null",
984 SingleChangeToDescription(*changes1()));
991 ASSERT_TRUE(DeleteView(vm2(), view_2_2
));
992 EXPECT_TRUE(changes2()->empty());
994 vm_client1_
->WaitForChangeCount(1);
995 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2
),
996 SingleChangeToDescription(*changes1()));
1000 // Verifies DeleteView isn't allowed from a separate connection.
1001 TEST_F(ViewTreeAppTest
, DeleteViewFromAnotherConnectionDisallowed
) {
1002 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1003 EXPECT_FALSE(DeleteView(vm2(), BuildViewId(connection_id_1(), 1)));
1006 // Verifies if a view was deleted and then reused that other clients are
1007 // properly notified.
1008 TEST_F(ViewTreeAppTest
, ReuseDeletedViewId
) {
1009 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1010 Id view_1_1
= BuildViewId(connection_id_1(), 1);
1011 Id view_2_2
= vm_client2()->CreateView(2);
1012 ASSERT_TRUE(view_2_2
);
1016 changes1()->clear();
1017 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
1018 vm_client1_
->WaitForChangeCount(1);
1019 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2
) + " new_parent=" +
1020 IdToString(view_1_1
) + " old_parent=null",
1021 SingleChangeToDescription(*changes1()));
1022 EXPECT_EQ("[" + ViewParentToString(view_2_2
, view_1_1
) + "]",
1023 ChangeViewDescription(*changes1()));
1028 changes1()->clear();
1029 ASSERT_TRUE(DeleteView(vm2(), view_2_2
));
1031 vm_client1_
->WaitForChangeCount(1);
1032 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2
),
1033 SingleChangeToDescription(*changes1()));
1036 // Create 2 again, and add it back to 1. Should get the same notification.
1037 view_2_2
= vm_client2()->CreateView(2);
1038 ASSERT_TRUE(view_2_2
);
1040 changes1()->clear();
1041 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
1043 vm_client1_
->WaitForChangeCount(1);
1044 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2
) + " new_parent=" +
1045 IdToString(view_1_1
) + " old_parent=null",
1046 SingleChangeToDescription(*changes1()));
1047 EXPECT_EQ("[" + ViewParentToString(view_2_2
, view_1_1
) + "]",
1048 ChangeViewDescription(*changes1()));
1052 // Assertions for GetViewTree.
1053 TEST_F(ViewTreeAppTest
, GetViewTree
) {
1054 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1055 Id view_1_1
= BuildViewId(connection_id_1(), 1);
1057 // Create 11 in first connection and make it a child of 1.
1058 Id view_1_11
= vm_client1()->CreateView(11);
1059 ASSERT_TRUE(view_1_11
);
1060 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
1061 ASSERT_TRUE(AddView(vm1(), view_1_1
, view_1_11
));
1063 // Create two views in second connection, 2 and 3, both children of 1.
1064 Id view_2_2
= vm_client2()->CreateView(2);
1065 Id view_2_3
= vm_client2()->CreateView(3);
1066 ASSERT_TRUE(view_2_2
);
1067 ASSERT_TRUE(view_2_3
);
1068 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
1069 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_3
));
1071 // Verifies GetViewTree() on the root. The root connection sees all.
1073 std::vector
<TestView
> views
;
1074 GetViewTree(vm1(), root_view_id(), &views
);
1075 ASSERT_EQ(5u, views
.size());
1076 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId
),
1077 views
[0].ToString());
1078 EXPECT_EQ(ViewParentToString(view_1_1
, root_view_id()),
1079 views
[1].ToString());
1080 EXPECT_EQ(ViewParentToString(view_1_11
, view_1_1
), views
[2].ToString());
1081 EXPECT_EQ(ViewParentToString(view_2_2
, view_1_1
), views
[3].ToString());
1082 EXPECT_EQ(ViewParentToString(view_2_3
, view_1_1
), views
[4].ToString());
1085 // Verifies GetViewTree() on the view 1,1 from vm2(). vm2() sees 1,1 as 1,1
1086 // is vm2()'s root and vm2() sees all the views it created.
1088 std::vector
<TestView
> views
;
1089 GetViewTree(vm2(), view_1_1
, &views
);
1090 ASSERT_EQ(3u, views
.size());
1091 EXPECT_EQ(ViewParentToString(view_1_1
, kNullParentId
), views
[0].ToString());
1092 EXPECT_EQ(ViewParentToString(view_2_2
, view_1_1
), views
[1].ToString());
1093 EXPECT_EQ(ViewParentToString(view_2_3
, view_1_1
), views
[2].ToString());
1096 // Connection 2 shouldn't be able to get the root tree.
1098 std::vector
<TestView
> views
;
1099 GetViewTree(vm2(), root_view_id(), &views
);
1100 ASSERT_EQ(0u, views
.size());
1104 TEST_F(ViewTreeAppTest
, SetViewBounds
) {
1105 Id view_1_1
= vm_client1()->CreateView(1);
1106 ASSERT_TRUE(view_1_1
);
1107 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
1109 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1111 changes2()->clear();
1112 ASSERT_TRUE(SetViewBounds(vm1(), view_1_1
, 0, 0, 100, 100));
1114 vm_client2_
->WaitForChangeCount(1);
1115 EXPECT_EQ("BoundsChanged view=" + IdToString(view_1_1
) +
1116 " old_bounds=0,0 0x0 new_bounds=0,0 100x100",
1117 SingleChangeToDescription(*changes2()));
1119 // Should not be possible to change the bounds of a view created by another
1121 ASSERT_FALSE(SetViewBounds(vm2(), view_1_1
, 0, 0, 0, 0));
1124 // Verify AddView fails when trying to manipulate views in other roots.
1125 TEST_F(ViewTreeAppTest
, CantMoveViewsFromOtherRoot
) {
1126 // Create 1 and 2 in the first connection.
1127 Id view_1_1
= vm_client1()->CreateView(1);
1128 Id view_1_2
= vm_client1()->CreateView(2);
1129 ASSERT_TRUE(view_1_1
);
1130 ASSERT_TRUE(view_1_2
);
1132 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1134 // Try to move 2 to be a child of 1 from connection 2. This should fail as 2
1135 // should not be able to access 1.
1136 ASSERT_FALSE(AddView(vm2(), view_1_1
, view_1_2
));
1138 // Try to reparent 1 to the root. A connection is not allowed to reparent its
1140 ASSERT_FALSE(AddView(vm2(), root_view_id(), view_1_1
));
1143 // Verify RemoveViewFromParent fails for views that are descendants of the
1145 TEST_F(ViewTreeAppTest
, CantRemoveViewsInOtherRoots
) {
1146 // Create 1 and 2 in the first connection and parent both to the root.
1147 Id view_1_1
= vm_client1()->CreateView(1);
1148 Id view_1_2
= vm_client1()->CreateView(2);
1149 ASSERT_TRUE(view_1_1
);
1150 ASSERT_TRUE(view_1_2
);
1152 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
1153 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2
));
1155 // Establish the second connection and give it the root 1.
1156 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1158 // Connection 2 should not be able to remove view 2 or 1 from its parent.
1159 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_2
));
1160 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_1
));
1162 // Create views 10 and 11 in 2.
1163 Id view_2_10
= vm_client2()->CreateView(10);
1164 Id view_2_11
= vm_client2()->CreateView(11);
1165 ASSERT_TRUE(view_2_10
);
1166 ASSERT_TRUE(view_2_11
);
1169 ASSERT_TRUE(AddView(vm2(), view_2_10
, view_2_11
));
1170 // Remove 11 from 10.
1171 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11
));
1173 // Verify nothing was actually removed.
1175 std::vector
<TestView
> views
;
1176 GetViewTree(vm1(), root_view_id(), &views
);
1177 ASSERT_EQ(3u, views
.size());
1178 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId
),
1179 views
[0].ToString());
1180 EXPECT_EQ(ViewParentToString(view_1_1
, root_view_id()),
1181 views
[1].ToString());
1182 EXPECT_EQ(ViewParentToString(view_1_2
, root_view_id()),
1183 views
[2].ToString());
1187 // Verify GetViewTree fails for views that are not descendants of the roots.
1188 TEST_F(ViewTreeAppTest
, CantGetViewTreeOfOtherRoots
) {
1189 // Create 1 and 2 in the first connection and parent both to the root.
1190 Id view_1_1
= vm_client1()->CreateView(1);
1191 Id view_1_2
= vm_client1()->CreateView(2);
1192 ASSERT_TRUE(view_1_1
);
1193 ASSERT_TRUE(view_1_2
);
1195 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
1196 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2
));
1198 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1200 std::vector
<TestView
> views
;
1202 // Should get nothing for the root.
1203 GetViewTree(vm2(), root_view_id(), &views
);
1204 ASSERT_TRUE(views
.empty());
1206 // Should get nothing for view 2.
1207 GetViewTree(vm2(), view_1_2
, &views
);
1208 ASSERT_TRUE(views
.empty());
1210 // Should get view 1 if asked for.
1211 GetViewTree(vm2(), view_1_1
, &views
);
1212 ASSERT_EQ(1u, views
.size());
1213 EXPECT_EQ(ViewParentToString(view_1_1
, kNullParentId
), views
[0].ToString());
1216 TEST_F(ViewTreeAppTest
, EmbedWithSameViewId
) {
1217 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1218 changes2()->clear();
1220 Id view_1_1
= BuildViewId(connection_id_1(), 1);
1221 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1
));
1223 // Connection2 should have been told of the unembed and delete.
1225 vm_client2_
->WaitForChangeCount(2);
1226 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes2())[0]);
1227 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1
),
1228 ChangesToDescription1(*changes2())[1]);
1231 // Connection2 has no root. Verify it can't see view 1,1 anymore.
1233 std::vector
<TestView
> views
;
1234 GetViewTree(vm2(), view_1_1
, &views
);
1235 EXPECT_TRUE(views
.empty());
1239 TEST_F(ViewTreeAppTest
, EmbedWithSameViewId2
) {
1240 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1241 Id view_1_1
= BuildViewId(connection_id_1(), 1);
1242 changes2()->clear();
1244 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1
));
1246 // Connection2 should have been told about the unembed and delete.
1247 vm_client2_
->WaitForChangeCount(2);
1248 changes2()->clear();
1250 // Create a view in the third connection and parent it to the root.
1251 Id view_3_1
= vm_client3()->CreateView(1);
1252 ASSERT_TRUE(view_3_1
);
1253 ASSERT_TRUE(AddView(vm3(), view_1_1
, view_3_1
));
1255 // Connection 1 should have been told about the add (it owns the view).
1257 vm_client1_
->WaitForChangeCount(1);
1258 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_3_1
) + " new_parent=" +
1259 IdToString(view_1_1
) + " old_parent=null",
1260 SingleChangeToDescription(*changes1()));
1265 changes3()->clear();
1267 // We should get a new connection for the new embedding.
1268 scoped_ptr
<ViewTreeClientImpl
> connection4(
1269 EstablishConnectionViaEmbed(vm1(), view_1_1
, nullptr));
1270 ASSERT_TRUE(connection4
.get());
1271 EXPECT_EQ("[" + ViewParentToString(view_1_1
, kNullParentId
) + "]",
1272 ChangeViewDescription(*connection4
->tracker()->changes()));
1274 // And 3 should get an unembed and delete.
1275 vm_client3_
->WaitForChangeCount(2);
1276 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes3())[0]);
1277 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1
),
1278 ChangesToDescription1(*changes3())[1]);
1281 // vm3() has no root. Verify it can't see view 1,1 anymore.
1283 std::vector
<TestView
> views
;
1284 GetViewTree(vm3(), view_1_1
, &views
);
1285 EXPECT_TRUE(views
.empty());
1288 // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as
1289 // vm3() can no longer see 1,1.
1291 std::vector
<TestView
> views
;
1292 GetViewTree(vm1(), view_1_1
, &views
);
1293 ASSERT_EQ(1u, views
.size());
1294 EXPECT_EQ(ViewParentToString(view_1_1
, kNullParentId
), views
[0].ToString());
1297 // Verify vm3() can still see the view it created 3,1.
1299 std::vector
<TestView
> views
;
1300 GetViewTree(vm3(), view_3_1
, &views
);
1301 ASSERT_EQ(1u, views
.size());
1302 EXPECT_EQ(ViewParentToString(view_3_1
, kNullParentId
), views
[0].ToString());
1306 // Assertions for SetViewVisibility.
1307 TEST_F(ViewTreeAppTest
, SetViewVisibility
) {
1308 // Create 1 and 2 in the first connection and parent both to the root.
1309 Id view_1_1
= vm_client1()->CreateView(1);
1310 Id view_1_2
= vm_client1()->CreateView(2);
1311 ASSERT_TRUE(view_1_1
);
1312 ASSERT_TRUE(view_1_2
);
1314 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
1316 std::vector
<TestView
> views
;
1317 GetViewTree(vm1(), root_view_id(), &views
);
1318 ASSERT_EQ(2u, views
.size());
1319 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId
) +
1320 " visible=true drawn=true",
1321 views
[0].ToString2());
1322 EXPECT_EQ(ViewParentToString(view_1_1
, root_view_id()) +
1323 " visible=false drawn=false",
1324 views
[1].ToString2());
1327 // Show all the views.
1328 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1
, true));
1329 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2
, true));
1331 std::vector
<TestView
> views
;
1332 GetViewTree(vm1(), root_view_id(), &views
);
1333 ASSERT_EQ(2u, views
.size());
1334 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId
) +
1335 " visible=true drawn=true",
1336 views
[0].ToString2());
1337 EXPECT_EQ(ViewParentToString(view_1_1
, root_view_id()) +
1338 " visible=true drawn=true",
1339 views
[1].ToString2());
1343 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1
, false));
1345 std::vector
<TestView
> views
;
1346 GetViewTree(vm1(), view_1_1
, &views
);
1347 ASSERT_EQ(1u, views
.size());
1348 EXPECT_EQ(ViewParentToString(view_1_1
, root_view_id()) +
1349 " visible=false drawn=false",
1350 views
[0].ToString2());
1354 ASSERT_TRUE(AddView(vm1(), view_1_1
, view_1_2
));
1356 std::vector
<TestView
> views
;
1357 GetViewTree(vm1(), view_1_1
, &views
);
1358 ASSERT_EQ(2u, views
.size());
1359 EXPECT_EQ(ViewParentToString(view_1_1
, root_view_id()) +
1360 " visible=false drawn=false",
1361 views
[0].ToString2());
1363 ViewParentToString(view_1_2
, view_1_1
) + " visible=true drawn=false",
1364 views
[1].ToString2());
1368 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1
, true));
1370 std::vector
<TestView
> views
;
1371 GetViewTree(vm1(), view_1_1
, &views
);
1372 ASSERT_EQ(2u, views
.size());
1373 EXPECT_EQ(ViewParentToString(view_1_1
, root_view_id()) +
1374 " visible=true drawn=true",
1375 views
[0].ToString2());
1377 ViewParentToString(view_1_2
, view_1_1
) + " visible=true drawn=true",
1378 views
[1].ToString2());
1382 // Assertions for SetViewVisibility sending notifications.
1383 TEST_F(ViewTreeAppTest
, SetViewVisibilityNotifications
) {
1384 // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
1385 Id view_1_1
= vm_client1()->CreateView(1);
1386 ASSERT_TRUE(view_1_1
);
1387 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1
, true));
1388 Id view_1_2
= vm_client1()->CreateView(2);
1389 ASSERT_TRUE(view_1_2
);
1390 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2
, true));
1391 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
1392 ASSERT_TRUE(AddView(vm1(), view_1_1
, view_1_2
));
1394 // Establish the second connection at 1,2.
1395 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoot(view_1_2
));
1397 // Add 2,3 as a child of 1,2.
1398 Id view_2_3
= vm_client2()->CreateView(3);
1399 ASSERT_TRUE(view_2_3
);
1400 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3
, true));
1401 ASSERT_TRUE(AddView(vm2(), view_1_2
, view_2_3
));
1402 WaitForAllMessages(vm1());
1404 changes2()->clear();
1405 // Hide 1,2 from connection 1. Connection 2 should see this.
1406 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2
, false));
1408 vm_client2_
->WaitForChangeCount(1);
1410 "VisibilityChanged view=" + IdToString(view_1_2
) + " visible=false",
1411 SingleChangeToDescription(*changes2()));
1414 changes1()->clear();
1415 // Show 1,2 from connection 2, connection 1 should be notified.
1416 ASSERT_TRUE(SetViewVisibility(vm2(), view_1_2
, true));
1418 vm_client1_
->WaitForChangeCount(1);
1420 "VisibilityChanged view=" + IdToString(view_1_2
) + " visible=true",
1421 SingleChangeToDescription(*changes1()));
1424 changes2()->clear();
1425 // Hide 1,1, connection 2 should be told the draw state changed.
1426 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1
, false));
1428 vm_client2_
->WaitForChangeCount(1);
1429 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2
) + " drawn=false",
1430 SingleChangeToDescription(*changes2()));
1433 changes2()->clear();
1434 // Show 1,1 from connection 1. Connection 2 should see this.
1435 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1
, true));
1437 vm_client2_
->WaitForChangeCount(1);
1438 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2
) + " drawn=true",
1439 SingleChangeToDescription(*changes2()));
1442 // Change visibility of 2,3, connection 1 should see this.
1443 changes1()->clear();
1444 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3
, false));
1446 vm_client1_
->WaitForChangeCount(1);
1448 "VisibilityChanged view=" + IdToString(view_2_3
) + " visible=false",
1449 SingleChangeToDescription(*changes1()));
1452 changes2()->clear();
1453 // Remove 1,1 from the root, connection 2 should see drawn state changed.
1454 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1
));
1456 vm_client2_
->WaitForChangeCount(1);
1457 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2
) + " drawn=false",
1458 SingleChangeToDescription(*changes2()));
1461 changes2()->clear();
1462 // Add 1,1 back to the root, connection 2 should see drawn state changed.
1463 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
1465 vm_client2_
->WaitForChangeCount(1);
1466 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2
) + " drawn=true",
1467 SingleChangeToDescription(*changes2()));
1471 TEST_F(ViewTreeAppTest
, SetViewProperty
) {
1472 Id view_1_1
= vm_client1()->CreateView(1);
1473 ASSERT_TRUE(view_1_1
);
1475 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1476 changes2()->clear();
1478 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
1480 std::vector
<TestView
> views
;
1481 GetViewTree(vm1(), root_view_id(), &views
);
1482 ASSERT_EQ(2u, views
.size());
1483 EXPECT_EQ(root_view_id(), views
[0].view_id
);
1484 EXPECT_EQ(view_1_1
, views
[1].view_id
);
1485 ASSERT_EQ(0u, views
[1].properties
.size());
1488 // Set properties on 1.
1489 changes2()->clear();
1490 std::vector
<uint8_t> one(1, '1');
1491 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1
, "one", &one
));
1493 vm_client2_
->WaitForChangeCount(1);
1495 "PropertyChanged view=" + IdToString(view_1_1
) + " key=one value=1",
1496 SingleChangeToDescription(*changes2()));
1499 // Test that our properties exist in the view tree
1501 std::vector
<TestView
> views
;
1502 GetViewTree(vm1(), view_1_1
, &views
);
1503 ASSERT_EQ(1u, views
.size());
1504 ASSERT_EQ(1u, views
[0].properties
.size());
1505 EXPECT_EQ(one
, views
[0].properties
["one"]);
1508 changes2()->clear();
1509 // Set back to null.
1510 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1
, "one", NULL
));
1512 vm_client2_
->WaitForChangeCount(1);
1514 "PropertyChanged view=" + IdToString(view_1_1
) + " key=one value=NULL",
1515 SingleChangeToDescription(*changes2()));
1519 TEST_F(ViewTreeAppTest
, OnEmbeddedAppDisconnected
) {
1520 // Create connection 2 and 3.
1521 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1522 Id view_1_1
= BuildViewId(connection_id_1(), 1);
1523 Id view_2_2
= vm_client2()->CreateView(2);
1524 ASSERT_TRUE(view_2_2
);
1525 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
1526 changes2()->clear();
1527 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2
));
1529 // Connection 1 should get a hierarchy change for view_2_2.
1530 vm_client1_
->WaitForChangeCount(1);
1531 changes1()->clear();
1533 // Close connection 3. Connection 2 (which had previously embedded 3) should
1534 // be notified of this.
1535 vm_client3_
.reset();
1536 vm_client2_
->WaitForChangeCount(1);
1537 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_2_2
),
1538 SingleChangeToDescription(*changes2()));
1540 vm_client1_
->WaitForChangeCount(1);
1541 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_2_2
),
1542 SingleChangeToDescription(*changes1()));
1545 // Verifies when the parent of an Embed() is destroyed the embedded app gets
1546 // a ViewDeleted (and doesn't trigger a DCHECK).
1547 TEST_F(ViewTreeAppTest
, OnParentOfEmbedDisconnects
) {
1548 // Create connection 2 and 3.
1549 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1550 Id view_1_1
= BuildViewId(connection_id_1(), 1);
1551 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1
));
1552 Id view_2_2
= vm_client2()->CreateView(2);
1553 Id view_2_3
= vm_client2()->CreateView(3);
1554 ASSERT_TRUE(view_2_2
);
1555 ASSERT_TRUE(view_2_3
);
1556 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
1557 ASSERT_TRUE(AddView(vm2(), view_2_2
, view_2_3
));
1558 changes2()->clear();
1559 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3
));
1560 changes3()->clear();
1562 // Close connection 2. Connection 3 should get a delete (for its root).
1563 vm_client2_
.reset();
1564 vm_client3_
->WaitForChangeCount(1);
1565 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_3
),
1566 SingleChangeToDescription(*changes3()));
1569 // Verifies ViewTreeImpl doesn't incorrectly erase from its internal
1570 // map when a view from another connection with the same view_id is removed.
1571 TEST_F(ViewTreeAppTest
, DontCleanMapOnDestroy
) {
1572 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1573 Id view_1_1
= BuildViewId(connection_id_1(), 1);
1574 ASSERT_TRUE(vm_client2()->CreateView(1));
1575 changes1()->clear();
1576 vm_client2_
.reset();
1577 vm_client1_
->WaitForChangeCount(1);
1578 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_1_1
),
1579 SingleChangeToDescription(*changes1()));
1580 std::vector
<TestView
> views
;
1581 GetViewTree(vm1(), view_1_1
, &views
);
1582 EXPECT_FALSE(views
.empty());
1585 // Verifies Embed() works when supplying a ViewTreeClient.
1586 TEST_F(ViewTreeAppTest
, EmbedSupplyingViewTreeClient
) {
1587 ASSERT_TRUE(vm_client1()->CreateView(1));
1589 ViewTreeClientImpl
client2(application_impl());
1590 mojo::ViewTreeClientPtr client2_ptr
;
1591 mojo::Binding
<ViewTreeClient
> client2_binding(&client2
, &client2_ptr
);
1593 Embed(vm1(), BuildViewId(connection_id_1(), 1), client2_ptr
.Pass()));
1594 client2
.WaitForOnEmbed();
1595 EXPECT_EQ("OnEmbed",
1596 SingleChangeToDescription(*client2
.tracker()->changes()));
1599 TEST_F(ViewTreeAppTest
, EmbedFailsFromOtherConnection
) {
1600 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1602 Id view_1_1
= BuildViewId(connection_id_1(), 1);
1603 Id view_2_2
= vm_client2()->CreateView(2);
1604 ASSERT_TRUE(view_2_2
);
1605 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
1606 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2
));
1608 Id view_3_3
= vm_client3()->CreateView(3);
1609 ASSERT_TRUE(view_3_3
);
1610 ASSERT_TRUE(AddView(vm3(), view_2_2
, view_3_3
));
1612 // 2 should not be able to embed in view_3_3 as view_3_3 was not created by
1615 EmbedUrl(application_impl(), vm2(), application_impl()->url(), view_3_3
));
1618 // Verifies Embed() from window manager on another connections view works.
1619 TEST_F(ViewTreeAppTest
, EmbedFromOtherConnection
) {
1620 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1622 Id view_1_1
= BuildViewId(connection_id_1(), 1);
1623 Id view_2_2
= vm_client2()->CreateView(2);
1624 ASSERT_TRUE(view_2_2
);
1625 ASSERT_TRUE(AddView(vm2(), view_1_1
, view_2_2
));
1627 changes2()->clear();
1629 // Establish a third connection in view_2_2.
1630 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_2_2
));
1632 WaitForAllMessages(vm2());
1633 EXPECT_EQ(std::string(), SingleChangeToDescription(*changes2()));
1636 TEST_F(ViewTreeAppTest
, CantEmbedFromConnectionRoot
) {
1637 // Shouldn't be able to embed into the root.
1638 ASSERT_FALSE(EmbedUrl(application_impl(), vm1(), application_impl()->url(),
1641 // Even though the call above failed a ViewTreeClient was obtained. We need to
1642 // wait for it else we throw off the next connect.
1643 WaitForViewTreeClient();
1645 // Don't allow a connection to embed into its own root.
1646 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1647 EXPECT_FALSE(EmbedUrl(application_impl(), vm2(), application_impl()->url(),
1648 BuildViewId(connection_id_1(), 1)));
1650 // Need to wait for a ViewTreeClient for same reason as above.
1651 WaitForViewTreeClient();
1653 Id view_1_2
= vm_client1()->CreateView(2);
1654 ASSERT_TRUE(view_1_2
);
1655 ASSERT_TRUE(AddView(vm1(), BuildViewId(connection_id_1(), 1), view_1_2
));
1656 ASSERT_TRUE(vm_client3_
.get() == nullptr);
1657 vm_client3_
= EstablishConnectionViaEmbedWithPolicyBitmask(
1658 vm1(), view_1_2
, mojo::ViewTree::ACCESS_POLICY_EMBED_ROOT
, nullptr);
1659 ASSERT_TRUE(vm_client3_
.get() != nullptr);
1660 vm_client3_
->set_root_view(root_view_id());
1662 // view_1_2 is vm3's root, so even though v3 is an embed root it should not
1663 // be able to Embed into itself.
1665 EmbedUrl(application_impl(), vm3(), application_impl()->url(), view_1_2
));
1668 // TODO(sky): need to better track changes to initial connection. For example,
1669 // that SetBounsdViews/AddView and the like don't result in messages to the
1670 // originating connection.
1672 // TODO(sky): make sure coverage of what was
1673 // ViewManagerTest.SecondEmbedRoot_InitService and
1674 // ViewManagerTest.MultipleEmbedRootsBeforeWTHReady gets added to window manager
1677 } // namespace view_manager