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 "mojo/services/public/cpp/view_manager/view_manager.h"
7 #include "base/auto_reset.h"
9 #include "base/logging.h"
10 #include "mojo/application_manager/application_manager.h"
11 #include "mojo/public/cpp/application/application_connection.h"
12 #include "mojo/public/cpp/application/application_delegate.h"
13 #include "mojo/public/cpp/application/application_impl.h"
14 #include "mojo/public/cpp/application/service_provider_impl.h"
15 #include "mojo/public/interfaces/application/service_provider.mojom.h"
16 #include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
17 #include "mojo/services/public/cpp/view_manager/lib/view_private.h"
18 #include "mojo/services/public/cpp/view_manager/util.h"
19 #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
20 #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
21 #include "mojo/services/public/cpp/view_manager/view_observer.h"
22 #include "mojo/shell/shell_test_helper.h"
23 #include "testing/gtest/include/gtest/gtest.h"
28 const char kWindowManagerURL
[] = "mojo:window_manager";
29 const char kEmbeddedApp1URL
[] = "mojo:embedded_app_1";
31 base::RunLoop
* current_run_loop
= NULL
;
34 base::RunLoop run_loop
;
35 current_run_loop
= &run_loop
;
36 current_run_loop
->Run();
37 current_run_loop
= NULL
;
41 current_run_loop
->Quit();
44 class ConnectApplicationLoader
: public ApplicationLoader
,
45 public ApplicationDelegate
,
46 public ViewManagerDelegate
{
48 typedef base::Callback
<void(ViewManager
*, View
*)> LoadedCallback
;
50 explicit ConnectApplicationLoader(const LoadedCallback
& callback
)
51 : callback_(callback
), view_manager_client_factory_(this) {}
52 virtual ~ConnectApplicationLoader() {}
55 // Overridden from ApplicationLoader:
56 virtual void Load(ApplicationManager
* manager
,
58 scoped_refptr
<LoadCallbacks
> callbacks
) OVERRIDE
{
59 ScopedMessagePipeHandle shell_handle
= callbacks
->RegisterApplication();
60 if (!shell_handle
.is_valid())
62 scoped_ptr
<ApplicationImpl
> app(new ApplicationImpl(this,
63 shell_handle
.Pass()));
64 apps_
.push_back(app
.release());
67 virtual void OnApplicationError(ApplicationManager
* manager
,
68 const GURL
& url
) OVERRIDE
{}
70 virtual bool ConfigureIncomingConnection(ApplicationConnection
* connection
)
72 connection
->AddService(&view_manager_client_factory_
);
76 // Overridden from ViewManagerDelegate:
77 virtual void OnEmbed(ViewManager
* view_manager
,
79 ServiceProviderImpl
* exported_services
,
80 scoped_ptr
<ServiceProvider
> imported_services
) OVERRIDE
{
81 callback_
.Run(view_manager
, root
);
83 virtual void OnViewManagerDisconnected(ViewManager
* view_manager
) OVERRIDE
{}
85 ScopedVector
<ApplicationImpl
> apps_
;
86 LoadedCallback callback_
;
87 ViewManagerClientFactory view_manager_client_factory_
;
89 DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader
);
92 class BoundsChangeObserver
: public ViewObserver
{
94 explicit BoundsChangeObserver(View
* view
) : view_(view
) {}
95 virtual ~BoundsChangeObserver() {}
98 // Overridden from ViewObserver:
99 virtual void OnViewBoundsChanged(View
* view
,
100 const gfx::Rect
& old_bounds
,
101 const gfx::Rect
& new_bounds
) OVERRIDE
{
102 DCHECK_EQ(view
, view_
);
108 DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver
);
111 // Wait until the bounds of the supplied view change.
112 void WaitForBoundsToChange(View
* view
) {
113 BoundsChangeObserver
observer(view
);
114 view
->AddObserver(&observer
);
116 view
->RemoveObserver(&observer
);
119 // Spins a runloop until the tree beginning at |root| has |tree_size| views
120 // (including |root|).
121 class TreeSizeMatchesObserver
: public ViewObserver
{
123 TreeSizeMatchesObserver(View
* tree
, size_t tree_size
)
125 tree_size_(tree_size
) {}
126 virtual ~TreeSizeMatchesObserver() {}
128 bool IsTreeCorrectSize() {
129 return CountViews(tree_
) == tree_size_
;
133 // Overridden from ViewObserver:
134 virtual void OnTreeChanged(const TreeChangeParams
& params
) OVERRIDE
{
135 if (IsTreeCorrectSize())
139 size_t CountViews(const View
* view
) const {
141 View::Children::const_iterator it
= view
->children().begin();
142 for (; it
!= view
->children().end(); ++it
)
143 count
+= CountViews(*it
);
150 DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver
);
153 void WaitForTreeSizeToMatch(View
* view
, size_t tree_size
) {
154 TreeSizeMatchesObserver
observer(view
, tree_size
);
155 if (observer
.IsTreeCorrectSize())
157 view
->AddObserver(&observer
);
159 view
->RemoveObserver(&observer
);
162 // Utility class that waits for the destruction of some number of views and
164 class DestructionObserver
: public ViewObserver
{
166 // |views| or |views| can be NULL.
167 explicit DestructionObserver(std::set
<Id
>* views
) : views_(views
) {}
170 // Overridden from ViewObserver:
171 virtual void OnViewDestroyed(View
* view
) OVERRIDE
{
172 std::set
<Id
>::iterator it
= views_
->find(view
->id());
173 if (it
!= views_
->end())
180 return !views_
|| views_
->empty();
183 std::set
<Id
>* views_
;
185 DISALLOW_COPY_AND_ASSIGN(DestructionObserver
);
188 void WaitForDestruction(ViewManager
* view_manager
, std::set
<Id
>* views
) {
189 DestructionObserver
observer(views
);
192 for (std::set
<Id
>::const_iterator it
= views
->begin();
193 it
!= views
->end(); ++it
) {
194 view_manager
->GetViewById(*it
)->AddObserver(&observer
);
200 class OrderChangeObserver
: public ViewObserver
{
202 OrderChangeObserver(View
* view
) : view_(view
) {
203 view_
->AddObserver(this);
205 virtual ~OrderChangeObserver() {
206 view_
->RemoveObserver(this);
210 // Overridden from ViewObserver:
211 virtual void OnViewReordered(View
* view
,
213 OrderDirection direction
) OVERRIDE
{
214 DCHECK_EQ(view
, view_
);
220 DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver
);
223 void WaitForOrderChange(ViewManager
* view_manager
, View
* view
) {
224 OrderChangeObserver
observer(view
);
228 // Tracks a view's destruction. Query is_valid() for current state.
229 class ViewTracker
: public ViewObserver
{
231 explicit ViewTracker(View
* view
) : view_(view
) {
232 view_
->AddObserver(this);
234 virtual ~ViewTracker() {
236 view_
->RemoveObserver(this);
239 bool is_valid() const { return !!view_
; }
242 // Overridden from ViewObserver:
243 virtual void OnViewDestroyed(View
* view
) OVERRIDE
{
244 DCHECK_EQ(view
, view_
);
251 DISALLOW_COPY_AND_ASSIGN(ViewTracker
);
256 // ViewManager -----------------------------------------------------------------
258 // These tests model synchronization of two peer connections to the view manager
259 // service, that are given access to some root view.
261 class ViewManagerTest
: public testing::Test
{
264 : connect_loop_(NULL
),
265 loaded_view_manager_(NULL
),
266 window_manager_(NULL
),
270 ViewManager
* window_manager() { return window_manager_
; }
272 View
* CreateViewInParent(View
* parent
) {
273 ViewManager
* parent_manager
= ViewPrivate(parent
).view_manager();
274 View
* view
= View::Create(parent_manager
);
275 parent
->AddChild(view
);
279 // Embeds another version of the test app @ view.
280 ViewManager
* Embed(ViewManager
* view_manager
, View
* view
) {
281 DCHECK_EQ(view_manager
, ViewPrivate(view
).view_manager());
282 view
->Embed(kEmbeddedApp1URL
);
284 return GetLoadedViewManager();
287 ViewManager
* GetLoadedViewManager() {
288 ViewManager
* view_manager
= loaded_view_manager_
;
289 loaded_view_manager_
= NULL
;
293 void UnloadApplication(const GURL
& url
) {
294 test_helper_
.SetLoaderForURL(scoped_ptr
<ApplicationLoader
>(), url
);
298 // Overridden from testing::Test:
299 virtual void SetUp() OVERRIDE
{
300 ConnectApplicationLoader::LoadedCallback ready_callback
= base::Bind(
301 &ViewManagerTest::OnViewManagerLoaded
, base::Unretained(this));
303 test_helper_
.SetLoaderForURL(
304 scoped_ptr
<ApplicationLoader
>(
305 new ConnectApplicationLoader(ready_callback
)),
306 GURL(kWindowManagerURL
));
307 test_helper_
.SetLoaderForURL(
308 scoped_ptr
<ApplicationLoader
>(
309 new ConnectApplicationLoader(ready_callback
)),
310 GURL(kEmbeddedApp1URL
));
312 test_helper_
.application_manager()->ConnectToService(
313 GURL("mojo:mojo_view_manager"), &view_manager_init_
);
314 ASSERT_TRUE(EmbedRoot(view_manager_init_
.get(), kWindowManagerURL
));
317 void EmbedRootCallback(bool* result_cache
, bool result
) {
318 *result_cache
= result
;
321 bool EmbedRoot(ViewManagerInitService
* view_manager_init
,
322 const std::string
& url
) {
324 ServiceProviderPtr sp
;
325 BindToProxy(new ServiceProviderImpl
, &sp
);
326 view_manager_init
->Embed(
328 base::Bind(&ViewManagerTest::EmbedRootCallback
, base::Unretained(this),
331 window_manager_
= GetLoadedViewManager();
335 void OnViewManagerLoaded(ViewManager
* view_manager
, View
* root
) {
336 loaded_view_manager_
= view_manager
;
337 connect_loop_
->Quit();
341 base::RunLoop run_loop
;
342 connect_loop_
= &run_loop
;
343 connect_loop_
->Run();
344 connect_loop_
= NULL
;
347 base::RunLoop
* connect_loop_
;
348 shell::ShellTestHelper test_helper_
;
349 ViewManagerInitServicePtr view_manager_init_
;
350 // Used to receive the most recent view manager loaded by an embed action.
351 ViewManager
* loaded_view_manager_
;
352 // The View Manager connection held by the window manager (app running at the
354 ViewManager
* window_manager_
;
357 DISALLOW_COPY_AND_ASSIGN(ViewManagerTest
);
360 TEST_F(ViewManagerTest
, SetUp
) {}
362 TEST_F(ViewManagerTest
, Embed
) {
363 View
* view
= View::Create(window_manager());
364 window_manager()->GetRoots().front()->AddChild(view
);
365 ViewManager
* embedded
= Embed(window_manager(), view
);
366 EXPECT_TRUE(NULL
!= embedded
);
368 View
* view_in_embedded
= embedded
->GetRoots().front();
369 EXPECT_EQ(view
->parent(), window_manager()->GetRoots().front());
370 EXPECT_EQ(NULL
, view_in_embedded
->parent());
373 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see
375 // TODO(sky): Update client lib to match server.
376 TEST_F(ViewManagerTest
, DISABLED_EmbeddedDoesntSeeChild
) {
377 View
* view
= View::Create(window_manager());
378 window_manager()->GetRoots().front()->AddChild(view
);
379 View
* nested
= View::Create(window_manager());
380 view
->AddChild(nested
);
382 ViewManager
* embedded
= Embed(window_manager(), view
);
383 EXPECT_EQ(embedded
->GetRoots().front()->children().front()->id(),
385 EXPECT_TRUE(embedded
->GetRoots().front()->children().empty());
386 EXPECT_TRUE(nested
->parent() == NULL
);
389 // http://crbug.com/396300
390 TEST_F(ViewManagerTest
, DISABLED_ViewManagerDestroyed_CleanupView
) {
391 View
* view
= View::Create(window_manager());
392 window_manager()->GetRoots().front()->AddChild(view
);
393 ViewManager
* embedded
= Embed(window_manager(), view
);
395 Id view_id
= view
->id();
397 UnloadApplication(GURL(kWindowManagerURL
));
400 views
.insert(view_id
);
401 WaitForDestruction(embedded
, &views
);
403 EXPECT_TRUE(embedded
->GetRoots().empty());
406 // TODO(beng): write a replacement test for the one that once existed here:
407 // This test validates the following scenario:
408 // - a view originating from one connection
409 // - a view originating from a second connection
410 // + the connection originating the view is destroyed
411 // -> the view should still exist (since the second connection is live) but
412 // should be disconnected from any views.
413 // http://crbug.com/396300
415 // TODO(beng): The new test should validate the scenario as described above
416 // except that the second connection still has a valid tree.
418 // Verifies that bounds changes applied to a view hierarchy in one connection
419 // are reflected to another.
420 TEST_F(ViewManagerTest
, SetBounds
) {
421 View
* view
= View::Create(window_manager());
422 window_manager()->GetRoots().front()->AddChild(view
);
423 ViewManager
* embedded
= Embed(window_manager(), view
);
425 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
426 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
428 view
->SetBounds(gfx::Rect(100, 100));
429 EXPECT_NE(view
->bounds(), view_in_embedded
->bounds());
430 WaitForBoundsToChange(view_in_embedded
);
431 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
434 // Verifies that bounds changes applied to a view owned by a different
435 // connection are refused.
436 TEST_F(ViewManagerTest
, SetBoundsSecurity
) {
437 View
* view
= View::Create(window_manager());
438 window_manager()->GetRoots().front()->AddChild(view
);
439 ViewManager
* embedded
= Embed(window_manager(), view
);
441 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
442 view
->SetBounds(gfx::Rect(800, 600));
443 WaitForBoundsToChange(view_in_embedded
);
445 view_in_embedded
->SetBounds(gfx::Rect(1024, 768));
446 // Bounds change should have been rejected.
447 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
450 // Verifies that a view can only be destroyed by the connection that created it.
451 TEST_F(ViewManagerTest
, DestroySecurity
) {
452 View
* view
= View::Create(window_manager());
453 window_manager()->GetRoots().front()->AddChild(view
);
454 ViewManager
* embedded
= Embed(window_manager(), view
);
456 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
458 ViewTracker
tracker2(view_in_embedded
);
459 view_in_embedded
->Destroy();
460 // View should not have been destroyed.
461 EXPECT_TRUE(tracker2
.is_valid());
463 ViewTracker
tracker1(view
);
465 EXPECT_FALSE(tracker1
.is_valid());
468 TEST_F(ViewManagerTest
, MultiRoots
) {
469 View
* view1
= View::Create(window_manager());
470 window_manager()->GetRoots().front()->AddChild(view1
);
471 View
* view2
= View::Create(window_manager());
472 window_manager()->GetRoots().front()->AddChild(view2
);
473 ViewManager
* embedded1
= Embed(window_manager(), view1
);
474 ViewManager
* embedded2
= Embed(window_manager(), view2
);
475 EXPECT_EQ(embedded1
, embedded2
);
478 TEST_F(ViewManagerTest
, EmbeddingIdentity
) {
479 View
* view
= View::Create(window_manager());
480 window_manager()->GetRoots().front()->AddChild(view
);
481 ViewManager
* embedded
= Embed(window_manager(), view
);
482 EXPECT_EQ(kWindowManagerURL
, embedded
->GetEmbedderURL());
485 TEST_F(ViewManagerTest
, Reorder
) {
486 View
* view1
= View::Create(window_manager());
487 window_manager()->GetRoots().front()->AddChild(view1
);
489 ViewManager
* embedded
= Embed(window_manager(), view1
);
491 View
* view11
= View::Create(embedded
);
492 embedded
->GetRoots().front()->AddChild(view11
);
493 View
* view12
= View::Create(embedded
);
494 embedded
->GetRoots().front()->AddChild(view12
);
496 View
* view1_in_wm
= window_manager()->GetViewById(view1
->id());
499 WaitForTreeSizeToMatch(view1
, 2u);
500 view11
->MoveToFront();
501 WaitForOrderChange(window_manager(),
502 window_manager()->GetViewById(view11
->id()));
504 EXPECT_EQ(view1_in_wm
->children().front(),
505 window_manager()->GetViewById(view12
->id()));
506 EXPECT_EQ(view1_in_wm
->children().back(),
507 window_manager()->GetViewById(view11
->id()));
511 view11
->MoveToBack();
512 WaitForOrderChange(window_manager(),
513 window_manager()->GetViewById(view11
->id()));
515 EXPECT_EQ(view1_in_wm
->children().front(),
516 window_manager()->GetViewById(view11
->id()));
517 EXPECT_EQ(view1_in_wm
->children().back(),
518 window_manager()->GetViewById(view12
->id()));
522 // TODO(beng): tests for view event dispatcher.
523 // - verify that we see events for all views.
525 // TODO(beng): tests for focus:
526 // - focus between two views known to a connection
527 // - focus between views unknown to one of the connections.
528 // - focus between views unknown to either connection.