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_context.h"
17 #include "components/view_manager/public/cpp/view_manager_delegate.h"
18 #include "components/view_manager/public/cpp/view_observer.h"
19 #include "mojo/application/application_test_base_chromium.h"
20 #include "third_party/mojo/src/mojo/public/cpp/application/application_connection.h"
21 #include "third_party/mojo/src/mojo/public/cpp/application/application_delegate.h"
22 #include "third_party/mojo/src/mojo/public/cpp/application/application_impl.h"
23 #include "third_party/mojo/src/mojo/public/cpp/application/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 // Spins a run loop until the tree beginning at |root| has |tree_size| views
87 // (including |root|).
88 class TreeSizeMatchesObserver
: public ViewObserver
{
90 TreeSizeMatchesObserver(View
* tree
, size_t tree_size
)
91 : tree_(tree
), tree_size_(tree_size
) {
92 tree_
->AddObserver(this);
94 ~TreeSizeMatchesObserver() override
{ tree_
->RemoveObserver(this); }
96 bool IsTreeCorrectSize() { return CountViews(tree_
) == tree_size_
; }
99 // Overridden from ViewObserver:
100 void OnTreeChanged(const TreeChangeParams
& params
) override
{
101 if (IsTreeCorrectSize())
105 size_t CountViews(const View
* view
) const {
107 View::Children::const_iterator it
= view
->children().begin();
108 for (; it
!= view
->children().end(); ++it
)
109 count
+= CountViews(*it
);
116 MOJO_DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver
);
119 // Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
120 bool WaitForTreeSizeToMatch(View
* view
, size_t tree_size
) {
121 TreeSizeMatchesObserver
observer(view
, tree_size
);
122 return observer
.IsTreeCorrectSize() || DoRunLoopWithTimeout();
125 class OrderChangeObserver
: public ViewObserver
{
127 OrderChangeObserver(View
* view
) : view_(view
) { view_
->AddObserver(this); }
128 ~OrderChangeObserver() override
{ view_
->RemoveObserver(this); }
131 // Overridden from ViewObserver:
132 void OnViewReordered(View
* view
,
134 OrderDirection direction
) override
{
135 DCHECK_EQ(view
, view_
);
141 MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver
);
144 // Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
145 bool WaitForOrderChange(ViewManager
* view_manager
, View
* view
) {
146 OrderChangeObserver
observer(view
);
147 return DoRunLoopWithTimeout();
150 // Tracks a view's destruction. Query is_valid() for current state.
151 class ViewTracker
: public ViewObserver
{
153 explicit ViewTracker(View
* view
) : view_(view
) { view_
->AddObserver(this); }
154 ~ViewTracker() override
{
156 view_
->RemoveObserver(this);
159 bool is_valid() const { return !!view_
; }
162 // Overridden from ViewObserver:
163 void OnViewDestroyed(View
* view
) override
{
164 DCHECK_EQ(view
, view_
);
171 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTracker
);
176 // ViewManager -----------------------------------------------------------------
178 // These tests model synchronization of two peer connections to the view manager
179 // service, that are given access to some root view.
181 class ViewManagerTest
: public test::ApplicationTestBase
,
182 public ApplicationDelegate
,
183 public ViewManagerDelegate
{
186 : most_recent_view_manager_(nullptr), window_manager_(nullptr) {}
188 // Overridden from ApplicationDelegate:
189 void Initialize(ApplicationImpl
* app
) override
{
190 view_manager_client_factory_
.reset(
191 new ViewManagerClientFactory(app
->shell(), this));
194 // ApplicationDelegate implementation.
195 bool ConfigureIncomingConnection(ApplicationConnection
* connection
) override
{
196 connection
->AddService(view_manager_client_factory_
.get());
200 ViewManager
* window_manager() { return window_manager_
; }
202 // Embeds another version of the test app @ view; returns nullptr on timeout.
203 ViewManager
* Embed(ViewManager
* view_manager
, View
* view
) {
204 DCHECK_EQ(view_manager
, view
->view_manager());
205 most_recent_view_manager_
= nullptr;
206 view
->Embed(application_impl()->url());
207 if (!DoRunLoopWithTimeout())
209 ViewManager
* vm
= nullptr;
210 std::swap(vm
, most_recent_view_manager_
);
214 ApplicationDelegate
* GetApplicationDelegate() override
{ return this; }
216 // Overridden from ViewManagerDelegate:
217 void OnEmbed(View
* root
,
218 InterfaceRequest
<ServiceProvider
> services
,
219 ServiceProviderPtr exposed_services
) override
{
220 most_recent_view_manager_
= root
->view_manager();
223 void OnViewManagerDisconnected(ViewManager
* view_manager
) override
{}
226 // Overridden from testing::Test:
227 void SetUp() override
{
228 ApplicationTestBase::SetUp();
230 view_manager_context_
.reset(new ViewManagerContext(application_impl()));
231 view_manager_context_
->Embed(application_impl()->url());
232 ASSERT_TRUE(DoRunLoopWithTimeout());
233 std::swap(window_manager_
, most_recent_view_manager_
);
236 // Overridden from testing::Test:
237 void TearDown() override
{ ApplicationTestBase::TearDown(); }
239 scoped_ptr
<ViewManagerClientFactory
> view_manager_client_factory_
;
241 scoped_ptr
<ViewManagerContext
> view_manager_context_
;
243 // Used to receive the most recent view manager loaded by an embed action.
244 ViewManager
* most_recent_view_manager_
;
245 // The View Manager connection held by the window manager (app running at the
247 ViewManager
* window_manager_
;
249 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTest
);
252 TEST_F(ViewManagerTest
, RootView
) {
253 ASSERT_NE(nullptr, window_manager());
254 EXPECT_NE(nullptr, window_manager()->GetRoot());
255 EXPECT_EQ("mojo:window_manager", window_manager()->GetEmbedderURL());
258 TEST_F(ViewManagerTest
, Embed
) {
259 View
* view
= window_manager()->CreateView();
260 ASSERT_NE(nullptr, view
);
261 view
->SetVisible(true);
262 window_manager()->GetRoot()->AddChild(view
);
263 ViewManager
* embedded
= Embed(window_manager(), view
);
264 ASSERT_NE(nullptr, embedded
);
266 View
* view_in_embedded
= embedded
->GetRoot();
267 ASSERT_NE(nullptr, view_in_embedded
);
268 EXPECT_EQ(view
->id(), view_in_embedded
->id());
269 EXPECT_EQ(nullptr, view_in_embedded
->parent());
270 EXPECT_TRUE(view_in_embedded
->children().empty());
273 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see
275 TEST_F(ViewManagerTest
, EmbeddedDoesntSeeChild
) {
276 View
* view
= window_manager()->CreateView();
277 ASSERT_NE(nullptr, view
);
278 view
->SetVisible(true);
279 window_manager()->GetRoot()->AddChild(view
);
280 View
* nested
= window_manager()->CreateView();
281 ASSERT_NE(nullptr, nested
);
282 nested
->SetVisible(true);
283 view
->AddChild(nested
);
285 ViewManager
* embedded
= Embed(window_manager(), view
);
286 ASSERT_NE(nullptr, embedded
);
287 View
* view_in_embedded
= embedded
->GetRoot();
288 EXPECT_EQ(view
->id(), view_in_embedded
->id());
289 EXPECT_EQ(nullptr, view_in_embedded
->parent());
290 EXPECT_TRUE(view_in_embedded
->children().empty());
293 // TODO(beng): write a replacement test for the one that once existed here:
294 // This test validates the following scenario:
295 // - a view originating from one connection
296 // - a view originating from a second connection
297 // + the connection originating the view is destroyed
298 // -> the view should still exist (since the second connection is live) but
299 // should be disconnected from any views.
300 // http://crbug.com/396300
302 // TODO(beng): The new test should validate the scenario as described above
303 // except that the second connection still has a valid tree.
305 // Verifies that bounds changes applied to a view hierarchy in one connection
306 // are reflected to another.
307 TEST_F(ViewManagerTest
, SetBounds
) {
308 View
* view
= window_manager()->CreateView();
309 view
->SetVisible(true);
310 window_manager()->GetRoot()->AddChild(view
);
311 ViewManager
* embedded
= Embed(window_manager(), view
);
312 ASSERT_NE(nullptr, embedded
);
314 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
315 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
318 rect
.width
= rect
.height
= 100;
319 view
->SetBounds(rect
);
320 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded
));
321 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
324 // Verifies that bounds changes applied to a view owned by a different
325 // connection are refused.
326 TEST_F(ViewManagerTest
, SetBoundsSecurity
) {
327 View
* view
= window_manager()->CreateView();
328 view
->SetVisible(true);
329 window_manager()->GetRoot()->AddChild(view
);
330 ViewManager
* embedded
= Embed(window_manager(), view
);
331 ASSERT_NE(nullptr, embedded
);
333 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
337 view
->SetBounds(rect
);
338 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded
));
342 view_in_embedded
->SetBounds(rect
);
343 // Bounds change should have been rejected.
344 EXPECT_EQ(view
->bounds(), view_in_embedded
->bounds());
347 // Verifies that a view can only be destroyed by the connection that created it.
348 TEST_F(ViewManagerTest
, DestroySecurity
) {
349 View
* view
= window_manager()->CreateView();
350 view
->SetVisible(true);
351 window_manager()->GetRoot()->AddChild(view
);
352 ViewManager
* embedded
= Embed(window_manager(), view
);
353 ASSERT_NE(nullptr, embedded
);
355 View
* view_in_embedded
= embedded
->GetViewById(view
->id());
357 ViewTracker
tracker2(view_in_embedded
);
358 view_in_embedded
->Destroy();
359 // View should not have been destroyed.
360 EXPECT_TRUE(tracker2
.is_valid());
362 ViewTracker
tracker1(view
);
364 EXPECT_FALSE(tracker1
.is_valid());
367 TEST_F(ViewManagerTest
, MultiRoots
) {
368 View
* view1
= window_manager()->CreateView();
369 view1
->SetVisible(true);
370 window_manager()->GetRoot()->AddChild(view1
);
371 View
* view2
= window_manager()->CreateView();
372 view2
->SetVisible(true);
373 window_manager()->GetRoot()->AddChild(view2
);
374 ViewManager
* embedded1
= Embed(window_manager(), view1
);
375 ASSERT_NE(nullptr, embedded1
);
376 ViewManager
* embedded2
= Embed(window_manager(), view2
);
377 ASSERT_NE(nullptr, embedded2
);
378 EXPECT_NE(embedded1
, embedded2
);
381 TEST_F(ViewManagerTest
, EmbeddingIdentity
) {
382 View
* view
= window_manager()->CreateView();
383 view
->SetVisible(true);
384 window_manager()->GetRoot()->AddChild(view
);
385 ViewManager
* embedded
= Embed(window_manager(), view
);
386 ASSERT_NE(nullptr, embedded
);
387 EXPECT_EQ(application_impl()->url(), embedded
->GetEmbedderURL());
390 // TODO(alhaad): Currently, the RunLoop gets stuck waiting for order change.
391 // Debug and re-enable this.
392 TEST_F(ViewManagerTest
, DISABLED_Reorder
) {
393 View
* view1
= window_manager()->CreateView();
394 view1
->SetVisible(true);
395 window_manager()->GetRoot()->AddChild(view1
);
397 ViewManager
* embedded
= Embed(window_manager(), view1
);
398 ASSERT_NE(nullptr, embedded
);
400 View
* view11
= embedded
->CreateView();
401 view11
->SetVisible(true);
402 embedded
->GetRoot()->AddChild(view11
);
403 View
* view12
= embedded
->CreateView();
404 view12
->SetVisible(true);
405 embedded
->GetRoot()->AddChild(view12
);
407 View
* root_in_embedded
= embedded
->GetRoot();
410 ASSERT_TRUE(WaitForTreeSizeToMatch(root_in_embedded
, 3u));
411 view11
->MoveToFront();
412 ASSERT_TRUE(WaitForOrderChange(embedded
, root_in_embedded
));
414 EXPECT_EQ(root_in_embedded
->children().front(),
415 embedded
->GetViewById(view12
->id()));
416 EXPECT_EQ(root_in_embedded
->children().back(),
417 embedded
->GetViewById(view11
->id()));
421 view11
->MoveToBack();
422 ASSERT_TRUE(WaitForOrderChange(embedded
,
423 embedded
->GetViewById(view11
->id())));
425 EXPECT_EQ(root_in_embedded
->children().front(),
426 embedded
->GetViewById(view11
->id()));
427 EXPECT_EQ(root_in_embedded
->children().back(),
428 embedded
->GetViewById(view12
->id()));
434 class VisibilityChangeObserver
: public ViewObserver
{
436 explicit VisibilityChangeObserver(View
* view
) : view_(view
) {
437 view_
->AddObserver(this);
439 ~VisibilityChangeObserver() override
{ view_
->RemoveObserver(this); }
442 // Overridden from ViewObserver:
443 void OnViewVisibilityChanged(View
* view
) override
{
444 EXPECT_EQ(view
, view_
);
450 MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver
);
455 TEST_F(ViewManagerTest
, Visible
) {
456 View
* view1
= window_manager()->CreateView();
457 view1
->SetVisible(true);
458 window_manager()->GetRoot()->AddChild(view1
);
460 // Embed another app and verify initial state.
461 ViewManager
* embedded
= Embed(window_manager(), view1
);
462 ASSERT_NE(nullptr, embedded
);
463 ASSERT_NE(nullptr, embedded
->GetRoot());
464 View
* embedded_root
= embedded
->GetRoot();
465 EXPECT_TRUE(embedded_root
->visible());
466 EXPECT_TRUE(embedded_root
->IsDrawn());
468 // Change the visible state from the first connection and verify its mirrored
469 // correctly to the embedded app.
471 VisibilityChangeObserver
observer(embedded_root
);
472 view1
->SetVisible(false);
473 ASSERT_TRUE(DoRunLoopWithTimeout());
476 EXPECT_FALSE(view1
->visible());
477 EXPECT_FALSE(view1
->IsDrawn());
479 EXPECT_FALSE(embedded_root
->visible());
480 EXPECT_FALSE(embedded_root
->IsDrawn());
482 // Make the node visible again.
484 VisibilityChangeObserver
observer(embedded_root
);
485 view1
->SetVisible(true);
486 ASSERT_TRUE(DoRunLoopWithTimeout());
489 EXPECT_TRUE(view1
->visible());
490 EXPECT_TRUE(view1
->IsDrawn());
492 EXPECT_TRUE(embedded_root
->visible());
493 EXPECT_TRUE(embedded_root
->IsDrawn());
498 class DrawnChangeObserver
: public ViewObserver
{
500 explicit DrawnChangeObserver(View
* view
) : view_(view
) {
501 view_
->AddObserver(this);
503 ~DrawnChangeObserver() override
{ view_
->RemoveObserver(this); }
506 // Overridden from ViewObserver:
507 void OnViewDrawnChanged(View
* view
) override
{
508 EXPECT_EQ(view
, view_
);
514 MOJO_DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver
);
519 TEST_F(ViewManagerTest
, Drawn
) {
520 View
* view1
= window_manager()->CreateView();
521 view1
->SetVisible(true);
522 window_manager()->GetRoot()->AddChild(view1
);
524 // Embed another app and verify initial state.
525 ViewManager
* embedded
= Embed(window_manager(), view1
);
526 ASSERT_NE(nullptr, embedded
);
527 ASSERT_NE(nullptr, embedded
->GetRoot());
528 View
* embedded_root
= embedded
->GetRoot();
529 EXPECT_TRUE(embedded_root
->visible());
530 EXPECT_TRUE(embedded_root
->IsDrawn());
532 // Change the visibility of the root, this should propagate a drawn state
533 // change to |embedded|.
535 DrawnChangeObserver
observer(embedded_root
);
536 window_manager()->GetRoot()->SetVisible(false);
537 ASSERT_TRUE(DoRunLoopWithTimeout());
540 EXPECT_TRUE(view1
->visible());
541 EXPECT_FALSE(view1
->IsDrawn());
543 EXPECT_TRUE(embedded_root
->visible());
544 EXPECT_FALSE(embedded_root
->IsDrawn());
547 // TODO(beng): tests for view event dispatcher.
548 // - verify that we see events for all views.
552 class FocusChangeObserver
: public ViewObserver
{
554 explicit FocusChangeObserver(View
* view
)
555 : view_(view
), last_gained_focus_(nullptr), last_lost_focus_(nullptr) {
556 view_
->AddObserver(this);
558 ~FocusChangeObserver() override
{ view_
->RemoveObserver(this); }
560 View
* last_gained_focus() { return last_gained_focus_
; }
562 View
* last_lost_focus() { return last_lost_focus_
; }
565 // Overridden from ViewObserver.
566 void OnViewFocusChanged(View
* gained_focus
, View
* lost_focus
) override
{
567 last_gained_focus_
= gained_focus
;
568 last_lost_focus_
= lost_focus
;
573 View
* last_gained_focus_
;
574 View
* last_lost_focus_
;
576 MOJO_DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver
);
581 TEST_F(ViewManagerTest
, Focus
) {
582 View
* view1
= window_manager()->CreateView();
583 view1
->SetVisible(true);
584 window_manager()->GetRoot()->AddChild(view1
);
586 ViewManager
* embedded
= Embed(window_manager(), view1
);
587 ASSERT_NE(nullptr, embedded
);
588 View
* view11
= embedded
->CreateView();
589 view11
->SetVisible(true);
590 embedded
->GetRoot()->AddChild(view11
);
592 // TODO(alhaad): Figure out why switching focus between views from different
593 // connections is causing the tests to crash and add tests for that.
595 View
* embedded_root
= embedded
->GetRoot();
596 FocusChangeObserver
observer(embedded_root
);
597 embedded_root
->SetFocus();
598 ASSERT_TRUE(DoRunLoopWithTimeout());
599 ASSERT_NE(nullptr, observer
.last_gained_focus());
600 EXPECT_EQ(embedded_root
->id(), observer
.last_gained_focus()->id());
603 FocusChangeObserver
observer(view11
);
605 ASSERT_TRUE(DoRunLoopWithTimeout());
606 ASSERT_NE(nullptr, observer
.last_gained_focus());
607 ASSERT_NE(nullptr, observer
.last_lost_focus());
608 EXPECT_EQ(view11
->id(), observer
.last_gained_focus()->id());
609 EXPECT_EQ(embedded
->GetRoot()->id(), observer
.last_lost_focus()->id());