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. 4 5#include "mojo/services/public/cpp/view_manager/view_manager.h" 6 7#include "base/auto_reset.h" 8#include "base/bind.h" 9#include "base/logging.h" 10#include "mojo/public/cpp/application/application.h" 11#include "mojo/service_manager/service_manager.h" 12#include "mojo/services/public/cpp/view_manager/lib/node_private.h" 13#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h" 14#include "mojo/services/public/cpp/view_manager/node_observer.h" 15#include "mojo/services/public/cpp/view_manager/util.h" 16#include "mojo/services/public/cpp/view_manager/view.h" 17#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" 18#include "mojo/services/public/cpp/view_manager/view_observer.h" 19#include "mojo/shell/shell_test_helper.h" 20#include "testing/gtest/include/gtest/gtest.h" 21 22namespace mojo { 23namespace view_manager { 24namespace { 25 26const char kWindowManagerURL[] = "mojo:window_manager"; 27const char kEmbeddedApp1URL[] = "mojo:embedded_app_1"; 28 29base::RunLoop* current_run_loop = NULL; 30 31void DoRunLoop() { 32 base::RunLoop run_loop; 33 current_run_loop = &run_loop; 34 current_run_loop->Run(); 35 current_run_loop = NULL; 36} 37 38void QuitRunLoop() { 39 current_run_loop->Quit(); 40} 41 42void WaitForAllChangesToBeAcked(ViewManagerClientImpl* client) { 43 client->set_changes_acked_callback(base::Bind(&QuitRunLoop)); 44 DoRunLoop(); 45 client->ClearChangesAckedCallback(); 46} 47 48class ConnectServiceLoader : public ServiceLoader, 49 public ViewManagerDelegate { 50 public: 51 typedef base::Callback<void(ViewManager*, Node*)> LoadedCallback; 52 53 explicit ConnectServiceLoader(const LoadedCallback& callback) 54 : callback_(callback) { 55 } 56 virtual ~ConnectServiceLoader() {} 57 58 private: 59 // Overridden from ServiceLoader: 60 virtual void LoadService(ServiceManager* manager, 61 const GURL& url, 62 ScopedMessagePipeHandle shell_handle) OVERRIDE { 63 scoped_ptr<Application> app(new Application(shell_handle.Pass())); 64 ViewManager::Create(app.get(), this); 65 apps_.push_back(app.release()); 66 } 67 virtual void OnServiceError(ServiceManager* manager, 68 const GURL& url) OVERRIDE { 69 } 70 71 // Overridden from ViewManagerDelegate: 72 virtual void OnRootAdded(ViewManager* view_manager, 73 Node* root) OVERRIDE { 74 callback_.Run(view_manager, root); 75 } 76 77 ScopedVector<Application> apps_; 78 LoadedCallback callback_; 79 80 DISALLOW_COPY_AND_ASSIGN(ConnectServiceLoader); 81}; 82 83class ActiveViewChangedObserver : public NodeObserver { 84 public: 85 explicit ActiveViewChangedObserver(Node* node) 86 : node_(node) {} 87 virtual ~ActiveViewChangedObserver() {} 88 89 private: 90 // Overridden from NodeObserver: 91 virtual void OnNodeActiveViewChange(Node* node, 92 View* old_view, 93 View* new_view, 94 DispositionChangePhase phase) OVERRIDE { 95 DCHECK_EQ(node, node_); 96 QuitRunLoop(); 97 } 98 99 Node* node_; 100 101 DISALLOW_COPY_AND_ASSIGN(ActiveViewChangedObserver); 102}; 103 104// Waits until the active view id of the supplied node changes. 105void WaitForActiveViewToChange(Node* node) { 106 ActiveViewChangedObserver observer(node); 107 node->AddObserver(&observer); 108 DoRunLoop(); 109 node->RemoveObserver(&observer); 110} 111 112class BoundsChangeObserver : public NodeObserver { 113 public: 114 explicit BoundsChangeObserver(Node* node) : node_(node) {} 115 virtual ~BoundsChangeObserver() {} 116 117 private: 118 // Overridden from NodeObserver: 119 virtual void OnNodeBoundsChange(Node* node, 120 const gfx::Rect& old_bounds, 121 const gfx::Rect& new_bounds, 122 DispositionChangePhase phase) OVERRIDE { 123 DCHECK_EQ(node, node_); 124 if (phase != NodeObserver::DISPOSITION_CHANGED) 125 return; 126 QuitRunLoop(); 127 } 128 129 Node* node_; 130 131 DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); 132}; 133 134// Wait until the bounds of the supplied node change. 135void WaitForBoundsToChange(Node* node) { 136 BoundsChangeObserver observer(node); 137 node->AddObserver(&observer); 138 DoRunLoop(); 139 node->RemoveObserver(&observer); 140} 141 142// Spins a runloop until the tree beginning at |root| has |tree_size| nodes 143// (including |root|). 144class TreeSizeMatchesObserver : public NodeObserver { 145 public: 146 TreeSizeMatchesObserver(Node* tree, size_t tree_size) 147 : tree_(tree), 148 tree_size_(tree_size) {} 149 virtual ~TreeSizeMatchesObserver() {} 150 151 bool IsTreeCorrectSize() { 152 return CountNodes(tree_) == tree_size_; 153 } 154 155 private: 156 // Overridden from NodeObserver: 157 virtual void OnTreeChange(const TreeChangeParams& params) OVERRIDE { 158 if (IsTreeCorrectSize()) 159 QuitRunLoop(); 160 } 161 162 size_t CountNodes(const Node* node) const { 163 size_t count = 1; 164 Node::Children::const_iterator it = node->children().begin(); 165 for (; it != node->children().end(); ++it) 166 count += CountNodes(*it); 167 return count; 168 } 169 170 Node* tree_; 171 size_t tree_size_; 172 173 DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver); 174}; 175 176void WaitForTreeSizeToMatch(Node* node, size_t tree_size) { 177 TreeSizeMatchesObserver observer(node, tree_size); 178 if (observer.IsTreeCorrectSize()) 179 return; 180 node->AddObserver(&observer); 181 DoRunLoop(); 182 node->RemoveObserver(&observer); 183} 184 185 186// Utility class that waits for the destruction of some number of nodes and 187// views. 188class DestructionObserver : public NodeObserver, public ViewObserver { 189 public: 190 // |nodes| or |views| can be NULL. 191 DestructionObserver(std::set<Id>* nodes, std::set<Id>* views) 192 : nodes_(nodes), 193 views_(views) {} 194 195 private: 196 // Overridden from NodeObserver: 197 virtual void OnNodeDestroy( 198 Node* node, 199 NodeObserver::DispositionChangePhase phase) OVERRIDE { 200 if (phase != NodeObserver::DISPOSITION_CHANGED) 201 return; 202 std::set<Id>::iterator it = nodes_->find(node->id()); 203 if (it != nodes_->end()) 204 nodes_->erase(it); 205 if (CanQuit()) 206 QuitRunLoop(); 207 } 208 209 // Overridden from ViewObserver: 210 virtual void OnViewDestroy( 211 View* view, 212 ViewObserver::DispositionChangePhase phase) OVERRIDE { 213 if (phase != ViewObserver::DISPOSITION_CHANGED) 214 return; 215 std::set<Id>::iterator it = views_->find(view->id()); 216 if (it != views_->end()) 217 views_->erase(it); 218 if (CanQuit()) 219 QuitRunLoop(); 220 } 221 222 bool CanQuit() { 223 return (!nodes_ || nodes_->empty()) && (!views_ || views_->empty()); 224 } 225 226 std::set<Id>* nodes_; 227 std::set<Id>* views_; 228 229 DISALLOW_COPY_AND_ASSIGN(DestructionObserver); 230}; 231 232void WaitForDestruction(ViewManager* view_manager, 233 std::set<Id>* nodes, 234 std::set<Id>* views) { 235 DestructionObserver observer(nodes, views); 236 DCHECK(nodes || views); 237 if (nodes) { 238 for (std::set<Id>::const_iterator it = nodes->begin(); 239 it != nodes->end(); ++it) { 240 view_manager->GetNodeById(*it)->AddObserver(&observer); 241 } 242 } 243 if (views) { 244 for (std::set<Id>::const_iterator it = views->begin(); 245 it != views->end(); ++it) { 246 view_manager->GetViewById(*it)->AddObserver(&observer); 247 } 248 } 249 DoRunLoop(); 250} 251 252class OrderChangeObserver : public NodeObserver { 253 public: 254 OrderChangeObserver(Node* node) : node_(node) { 255 node_->AddObserver(this); 256 } 257 virtual ~OrderChangeObserver() { 258 node_->RemoveObserver(this); 259 } 260 261 private: 262 // Overridden from NodeObserver: 263 virtual void OnNodeReordered(Node* node, 264 Node* relative_node, 265 OrderDirection direction, 266 DispositionChangePhase phase) OVERRIDE { 267 if (phase != NodeObserver::DISPOSITION_CHANGED) 268 return; 269 270 DCHECK_EQ(node, node_); 271 QuitRunLoop(); 272 } 273 274 Node* node_; 275 276 DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); 277}; 278 279void WaitForOrderChange(ViewManager* view_manager, Node* node) { 280 OrderChangeObserver observer(node); 281 DoRunLoop(); 282} 283 284// Tracks a node's destruction. Query is_valid() for current state. 285class NodeTracker : public NodeObserver { 286 public: 287 explicit NodeTracker(Node* node) : node_(node) { 288 node_->AddObserver(this); 289 } 290 virtual ~NodeTracker() { 291 if (node_) 292 node_->RemoveObserver(this); 293 } 294 295 bool is_valid() const { return !!node_; } 296 297 private: 298 // Overridden from NodeObserver: 299 virtual void OnNodeDestroy( 300 Node* node, 301 NodeObserver::DispositionChangePhase phase) OVERRIDE { 302 if (phase != NodeObserver::DISPOSITION_CHANGED) 303 return; 304 DCHECK_EQ(node, node_); 305 node_ = NULL; 306 } 307 308 int id_; 309 Node* node_; 310 311 DISALLOW_COPY_AND_ASSIGN(NodeTracker); 312}; 313 314} // namespace 315 316// ViewManager ----------------------------------------------------------------- 317 318// These tests model synchronization of two peer connections to the view manager 319// service, that are given access to some root node. 320 321class ViewManagerTest : public testing::Test { 322 public: 323 ViewManagerTest() 324 : connect_loop_(NULL), 325 loaded_view_manager_(NULL), 326 window_manager_(NULL), 327 commit_count_(0) {} 328 329 protected: 330 ViewManager* window_manager() { return window_manager_; } 331 332 Node* CreateNodeInParent(Node* parent) { 333 ViewManager* parent_manager = NodePrivate(parent).view_manager(); 334 Node* node = Node::Create(parent_manager); 335 parent->AddChild(node); 336 return node; 337 } 338 339 // Embeds another version of the test app @ node. 340 ViewManager* Embed(ViewManager* view_manager, Node* node) { 341 DCHECK_EQ(view_manager, NodePrivate(node).view_manager()); 342 node->Embed(kEmbeddedApp1URL); 343 RunRunLoop(); 344 return GetLoadedViewManager(); 345 } 346 347 // TODO(beng): remove these methods once all the tests are migrated. 348 void DestroyViewManager1() {} 349 ViewManager* view_manager_1() { return NULL; } 350 ViewManager* view_manager_2() { return NULL; } 351 352 ViewManager* GetLoadedViewManager() { 353 ViewManager* view_manager = loaded_view_manager_; 354 loaded_view_manager_ = NULL; 355 return view_manager; 356 } 357 358 void UnloadApplication(const GURL& url) { 359 test_helper_.SetLoaderForURL(scoped_ptr<ServiceLoader>(), url); 360 } 361 362 private: 363 // Overridden from testing::Test: 364 virtual void SetUp() OVERRIDE { 365 ConnectServiceLoader::LoadedCallback ready_callback = 366 base::Bind(&ViewManagerTest::OnViewManagerLoaded, 367 base::Unretained(this)); 368 test_helper_.Init(); 369 test_helper_.SetLoaderForURL( 370 scoped_ptr<ServiceLoader>(new ConnectServiceLoader(ready_callback)), 371 GURL(kWindowManagerURL)); 372 test_helper_.SetLoaderForURL( 373 scoped_ptr<ServiceLoader>(new ConnectServiceLoader(ready_callback)), 374 GURL(kEmbeddedApp1URL)); 375 376 ConnectToService(test_helper_.service_provider(), 377 "mojo:mojo_view_manager", 378 &view_manager_init_); 379 ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kWindowManagerURL)); 380 } 381 382 void EmbedRootCallback(bool* result_cache, bool result) { 383 *result_cache = result; 384 } 385 386 bool EmbedRoot(ViewManagerInitService* view_manager_init, 387 const std::string& url) { 388 bool result = false; 389 view_manager_init->EmbedRoot( 390 url, 391 base::Bind(&ViewManagerTest::EmbedRootCallback, base::Unretained(this), 392 &result)); 393 RunRunLoop(); 394 window_manager_ = GetLoadedViewManager(); 395 return result; 396 } 397 398 void OnViewManagerLoaded(ViewManager* view_manager, Node* root) { 399 loaded_view_manager_ = view_manager; 400 connect_loop_->Quit(); 401 } 402 403 void RunRunLoop() { 404 base::RunLoop run_loop; 405 connect_loop_ = &run_loop; 406 connect_loop_->Run(); 407 connect_loop_ = NULL; 408 } 409 410 base::MessageLoop loop_; 411 base::RunLoop* connect_loop_; 412 shell::ShellTestHelper test_helper_; 413 ViewManagerInitServicePtr view_manager_init_; 414 // Used to receive the most recent view manager loaded by an embed action. 415 ViewManager* loaded_view_manager_; 416 // The View Manager connection held by the window manager (app running at the 417 // root node). 418 ViewManager* window_manager_; 419 int commit_count_; 420 421 DISALLOW_COPY_AND_ASSIGN(ViewManagerTest); 422}; 423 424TEST_F(ViewManagerTest, SetUp) {} 425 426TEST_F(ViewManagerTest, Embed) { 427 Node* node = Node::Create(window_manager()); 428 window_manager()->GetRoots().front()->AddChild(node); 429 ViewManager* embedded = Embed(window_manager(), node); 430 EXPECT_TRUE(NULL != embedded); 431 432 Node* node_in_embedded = embedded->GetRoots().front(); 433 EXPECT_EQ(node->parent(), window_manager()->GetRoots().front()); 434 EXPECT_EQ(NULL, node_in_embedded->parent()); 435} 436 437// When Window Manager embeds A @ N, then creates N2 and parents to N, N becomes 438// visible to A. 439// TODO(beng): verify whether or not this is a policy we like. 440TEST_F(ViewManagerTest, HierarchyChanged_NodeAdded) { 441 Node* node = Node::Create(window_manager()); 442 window_manager()->GetRoots().front()->AddChild(node); 443 ViewManager* embedded = Embed(window_manager(), node); 444 Node* nested = Node::Create(window_manager()); 445 node->AddChild(nested); 446 WaitForTreeSizeToMatch(embedded->GetRoots().front(), 2); 447 EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(), 448 nested->id()); 449} 450 451// Window manager has two nodes, N1 & N2. Embeds A at N1. Creates node N21, 452// a child of N2. Reparents N2 to N1. N1 should become visible to A. 453// TODO(beng): verify whether or not this is a policy we like. 454TEST_F(ViewManagerTest, HierarchyChanged_NodeMoved) { 455 Node* node1 = Node::Create(window_manager()); 456 window_manager()->GetRoots().front()->AddChild(node1); 457 ViewManager* embedded = Embed(window_manager(), node1); 458 WaitForTreeSizeToMatch(embedded->GetRoots().front(), 1); 459 460 Node* node2 = Node::Create(window_manager()); 461 window_manager()->GetRoots().front()->AddChild(node2); 462 WaitForTreeSizeToMatch(embedded->GetRoots().front(), 1); 463 EXPECT_TRUE(embedded->GetRoots().front()->children().empty()); 464 465 Node* node21 = Node::Create(window_manager()); 466 node2->AddChild(node21); 467 WaitForTreeSizeToMatch(embedded->GetRoots().front(), 1); 468 EXPECT_TRUE(embedded->GetRoots().front()->children().empty()); 469 470 // Makes node21 visible to |embedded|. 471 node1->AddChild(node21); 472 WaitForTreeSizeToMatch(embedded->GetRoots().front(), 2); 473 EXPECT_FALSE(embedded->GetRoots().front()->children().empty()); 474 EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(), 475 node21->id()); 476} 477 478// Window manager has two nodes, N1 and N11. Embeds A at N1. Removes N11 from 479// N1. N11 should disappear from A. 480// TODO(beng): verify whether or not this is a policy we like. 481TEST_F(ViewManagerTest, HierarchyChanged_NodeRemoved) { 482 Node* node = Node::Create(window_manager()); 483 window_manager()->GetRoots().front()->AddChild(node); 484 Node* nested = Node::Create(window_manager()); 485 node->AddChild(nested); 486 487 ViewManager* embedded = Embed(window_manager(), node); 488 EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(), 489 nested->id()); 490 491 node->RemoveChild(nested); 492 WaitForTreeSizeToMatch(embedded->GetRoots().front(), 1); 493 EXPECT_TRUE(embedded->GetRoots().front()->children().empty()); 494} 495 496// Window manager has two nodes, N1 and N11. Embeds A at N1. Destroys N11. 497// N11 should disappear from A. 498// TODO(beng): verify whether or not this is a policy we like. 499TEST_F(ViewManagerTest, NodeDestroyed) { 500 Node* node = Node::Create(window_manager()); 501 window_manager()->GetRoots().front()->AddChild(node); 502 Node* nested = Node::Create(window_manager()); 503 node->AddChild(nested); 504 505 ViewManager* embedded = Embed(window_manager(), node); 506 EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(), 507 nested->id()); 508 509 // |nested| will be deleted after calling Destroy() below. 510 Id id = nested->id(); 511 nested->Destroy(); 512 513 std::set<Id> nodes; 514 nodes.insert(id); 515 WaitForDestruction(embedded, &nodes, NULL); 516 517 EXPECT_TRUE(embedded->GetRoots().front()->children().empty()); 518 EXPECT_EQ(NULL, embedded->GetNodeById(id)); 519} 520 521TEST_F(ViewManagerTest, ViewManagerDestroyed_CleanupNode) { 522 Node* node = Node::Create(window_manager()); 523 window_manager()->GetRoots().front()->AddChild(node); 524 ViewManager* embedded = Embed(window_manager(), node); 525 526 Id node_id = node->id(); 527 528 UnloadApplication(GURL(kWindowManagerURL)); 529 530 std::set<Id> nodes; 531 nodes.insert(node_id); 532 WaitForDestruction(embedded, &nodes, NULL); 533 534 EXPECT_TRUE(embedded->GetRoots().empty()); 535} 536 537TEST_F(ViewManagerTest, SetActiveView) { 538 Node* node = Node::Create(window_manager()); 539 window_manager()->GetRoots().front()->AddChild(node); 540 ViewManager* embedded = Embed(window_manager(), node); 541 542 View* view = View::Create(window_manager()); 543 node->SetActiveView(view); 544 545 Node* node_in_embedded = embedded->GetNodeById(node->id()); 546 WaitForActiveViewToChange(node_in_embedded); 547 548 EXPECT_EQ(node_in_embedded->active_view()->id(), view->id()); 549} 550 551TEST_F(ViewManagerTest, DestroyView) { 552 Node* node = Node::Create(window_manager()); 553 window_manager()->GetRoots().front()->AddChild(node); 554 ViewManager* embedded = Embed(window_manager(), node); 555 556 View* view = View::Create(window_manager()); 557 node->SetActiveView(view); 558 559 Node* node_in_embedded = embedded->GetNodeById(node->id()); 560 WaitForActiveViewToChange(node_in_embedded); 561 562 EXPECT_EQ(node_in_embedded->active_view()->id(), view->id()); 563 564 Id view_id = view->id(); 565 view->Destroy(); 566 567 std::set<Id> views; 568 views.insert(view_id); 569 WaitForDestruction(embedded, NULL, &views); 570 EXPECT_EQ(NULL, node_in_embedded->active_view()); 571 EXPECT_EQ(NULL, embedded->GetViewById(view_id)); 572} 573 574// Destroying the connection that created a node and view should result in that 575// node and view disappearing from all connections that see them. 576TEST_F(ViewManagerTest, ViewManagerDestroyed_CleanupNodeAndView) { 577 Node* node = Node::Create(window_manager()); 578 window_manager()->GetRoots().front()->AddChild(node); 579 View* view = View::Create(window_manager()); 580 node->SetActiveView(view); 581 ViewManager* embedded = Embed(window_manager(), node); 582 583 Id node_id = node->id(); 584 Id view_id = view->id(); 585 586 UnloadApplication(GURL(kWindowManagerURL)); 587 588 std::set<Id> observed_nodes; 589 observed_nodes.insert(node_id); 590 std::set<Id> observed_views; 591 observed_views.insert(view_id); 592 WaitForDestruction(embedded, &observed_nodes, &observed_views); 593 594 EXPECT_TRUE(embedded->GetRoots().empty()); 595 EXPECT_EQ(NULL, embedded->GetNodeById(node_id)); 596 EXPECT_EQ(NULL, embedded->GetViewById(view_id)); 597} 598 599// This test validates the following scenario: 600// - a node originating from one connection 601// - a view originating from a second connection 602// + the connection originating the node is destroyed 603// -> the view should still exist (since the second connection is live) but 604// should be disconnected from any nodes. 605TEST_F(ViewManagerTest, 606 ViewManagerDestroyed_CleanupNodeAndViewFromDifferentConnections) { 607 Node* node = Node::Create(window_manager()); 608 window_manager()->GetRoots().front()->AddChild(node); 609 ViewManager* embedded = Embed(window_manager(), node); 610 View* view_in_embedded = View::Create(embedded); 611 Node* node_in_embedded = embedded->GetNodeById(node->id()); 612 node_in_embedded->SetActiveView(view_in_embedded); 613 614 WaitForActiveViewToChange(node); 615 616 Id node_id = node->id(); 617 Id view_id = view_in_embedded->id(); 618 619 UnloadApplication(GURL(kWindowManagerURL)); 620 std::set<Id> nodes; 621 nodes.insert(node_id); 622 WaitForDestruction(embedded, &nodes, NULL); 623 624 EXPECT_TRUE(embedded->GetRoots().empty()); 625 // node was owned by the window manager, so it should be gone. 626 EXPECT_EQ(NULL, embedded->GetNodeById(node_id)); 627 // view_in_embedded was owned by the embedded app, so it should still exist, 628 // but disconnected from the node tree. 629 EXPECT_EQ(view_in_embedded, embedded->GetViewById(view_id)); 630 EXPECT_EQ(NULL, view_in_embedded->node()); 631} 632 633// This test verifies that it is not possible to set the active view to a view 634// defined in a different connection. 635// TODO(beng): write these tests for Node::AddChild(), RemoveChild() and 636// Contains(). 637TEST_F(ViewManagerTest, SetActiveViewAcrossConnection) { 638 Node* node = Node::Create(window_manager()); 639 window_manager()->GetRoots().front()->AddChild(node); 640 ViewManager* embedded = Embed(window_manager(), node); 641 642 View* view_in_embedded = View::Create(embedded); 643 EXPECT_DEATH(node->SetActiveView(view_in_embedded), ""); 644} 645 646// This test verifies that a node hierarchy constructed in one connection 647// becomes entirely visible to the second connection when the hierarchy is 648// attached. 649TEST_F(ViewManagerTest, MapSubtreeOnAttach) { 650 Node* node = Node::Create(window_manager()); 651 window_manager()->GetRoots().front()->AddChild(node); 652 ViewManager* embedded = Embed(window_manager(), node); 653 654 // Create a subtree private to the window manager and make some changes to it. 655 Node* child1 = Node::Create(window_manager()); 656 Node* child11 = Node::Create(window_manager()); 657 child1->AddChild(child11); 658 gfx::Rect child11_bounds(800, 600); 659 child11->SetBounds(child11_bounds); 660 View* view11 = View::Create(window_manager()); 661 child11->SetActiveView(view11); 662 WaitForAllChangesToBeAcked( 663 static_cast<ViewManagerClientImpl*>(window_manager())); 664 665 // When added to the shared node, the entire hierarchy and all property 666 // changes should become visible to the embedded app. 667 node->AddChild(child1); 668 WaitForTreeSizeToMatch(embedded->GetRoots().front(), 3); 669 670 Node* child11_in_embedded = embedded->GetNodeById(child11->id()); 671 View* view11_in_embedded = embedded->GetViewById(view11->id()); 672 EXPECT_TRUE(child11_in_embedded != NULL); 673 EXPECT_EQ(view11_in_embedded, child11_in_embedded->active_view()); 674 EXPECT_EQ(child11_bounds, child11_in_embedded->bounds()); 675} 676 677// Verifies that bounds changes applied to a node hierarchy in one connection 678// are reflected to another. 679TEST_F(ViewManagerTest, SetBounds) { 680 Node* node = Node::Create(window_manager()); 681 window_manager()->GetRoots().front()->AddChild(node); 682 ViewManager* embedded = Embed(window_manager(), node); 683 684 Node* node_in_embedded = embedded->GetNodeById(node->id()); 685 EXPECT_EQ(node->bounds(), node_in_embedded->bounds()); 686 687 node->SetBounds(gfx::Rect(100, 100)); 688 EXPECT_NE(node->bounds(), node_in_embedded->bounds()); 689 WaitForBoundsToChange(node_in_embedded); 690 EXPECT_EQ(node->bounds(), node_in_embedded->bounds()); 691} 692 693// Verifies that bounds changes applied to a node owned by a different 694// connection are refused. 695TEST_F(ViewManagerTest, SetBoundsSecurity) { 696 Node* node = Node::Create(window_manager()); 697 window_manager()->GetRoots().front()->AddChild(node); 698 ViewManager* embedded = Embed(window_manager(), node); 699 700 Node* node_in_embedded = embedded->GetNodeById(node->id()); 701 node->SetBounds(gfx::Rect(800, 600)); 702 WaitForBoundsToChange(node_in_embedded); 703 704 node_in_embedded->SetBounds(gfx::Rect(1024, 768)); 705 // Bounds change should have been rejected. 706 EXPECT_EQ(node->bounds(), node_in_embedded->bounds()); 707} 708 709// Verifies that a node can only be destroyed by the connection that created it. 710TEST_F(ViewManagerTest, DestroySecurity) { 711 Node* node = Node::Create(window_manager()); 712 window_manager()->GetRoots().front()->AddChild(node); 713 ViewManager* embedded = Embed(window_manager(), node); 714 715 Node* node_in_embedded = embedded->GetNodeById(node->id()); 716 717 NodeTracker tracker2(node_in_embedded); 718 node_in_embedded->Destroy(); 719 // Node should not have been destroyed. 720 EXPECT_TRUE(tracker2.is_valid()); 721 722 NodeTracker tracker1(node); 723 node->Destroy(); 724 EXPECT_FALSE(tracker1.is_valid()); 725} 726 727TEST_F(ViewManagerTest, MultiRoots) { 728 Node* node1 = Node::Create(window_manager()); 729 window_manager()->GetRoots().front()->AddChild(node1); 730 Node* node2 = Node::Create(window_manager()); 731 window_manager()->GetRoots().front()->AddChild(node2); 732 ViewManager* embedded1 = Embed(window_manager(), node1); 733 ViewManager* embedded2 = Embed(window_manager(), node2); 734 EXPECT_EQ(embedded1, embedded2); 735} 736 737TEST_F(ViewManagerTest, EmbeddingIdentity) { 738 Node* node = Node::Create(window_manager()); 739 window_manager()->GetRoots().front()->AddChild(node); 740 ViewManager* embedded = Embed(window_manager(), node); 741 EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL()); 742} 743 744TEST_F(ViewManagerTest, Reorder) { 745 Node* node1 = Node::Create(window_manager()); 746 window_manager()->GetRoots().front()->AddChild(node1); 747 748 Node* node11 = Node::Create(window_manager()); 749 node1->AddChild(node11); 750 Node* node12 = Node::Create(window_manager()); 751 node1->AddChild(node12); 752 753 ViewManager* embedded = Embed(window_manager(), node1); 754 755 Node* node1_in_embedded = embedded->GetNodeById(node1->id()); 756 757 { 758 node11->MoveToFront(); 759 WaitForOrderChange(embedded, embedded->GetNodeById(node11->id())); 760 761 EXPECT_EQ(node1_in_embedded->children().front(), 762 embedded->GetNodeById(node12->id())); 763 EXPECT_EQ(node1_in_embedded->children().back(), 764 embedded->GetNodeById(node11->id())); 765 } 766 767 { 768 node11->MoveToBack(); 769 WaitForOrderChange(embedded, embedded->GetNodeById(node11->id())); 770 771 EXPECT_EQ(node1_in_embedded->children().front(), 772 embedded->GetNodeById(node11->id())); 773 EXPECT_EQ(node1_in_embedded->children().back(), 774 embedded->GetNodeById(node12->id())); 775 } 776} 777 778} // namespace view_manager 779} // namespace mojo 780