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/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" 24 25namespace mojo { 26namespace { 27 28const char kWindowManagerURL[] = "mojo:window_manager"; 29const char kEmbeddedApp1URL[] = "mojo:embedded_app_1"; 30 31base::RunLoop* current_run_loop = NULL; 32 33void DoRunLoop() { 34 base::RunLoop run_loop; 35 current_run_loop = &run_loop; 36 current_run_loop->Run(); 37 current_run_loop = NULL; 38} 39 40void QuitRunLoop() { 41 current_run_loop->Quit(); 42} 43 44class ConnectApplicationLoader : public ApplicationLoader, 45 public ApplicationDelegate, 46 public ViewManagerDelegate { 47 public: 48 typedef base::Callback<void(ViewManager*, View*)> LoadedCallback; 49 50 explicit ConnectApplicationLoader(const LoadedCallback& callback) 51 : callback_(callback) {} 52 virtual ~ConnectApplicationLoader() {} 53 54 private: 55 // Overridden from ApplicationDelegate: 56 virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE { 57 view_manager_client_factory_.reset( 58 new ViewManagerClientFactory(app->shell(), this)); 59 } 60 61 // Overridden from ApplicationLoader: 62 virtual void Load(ApplicationManager* manager, 63 const GURL& url, 64 scoped_refptr<LoadCallbacks> callbacks) OVERRIDE { 65 ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); 66 if (!shell_handle.is_valid()) 67 return; 68 scoped_ptr<ApplicationImpl> app(new ApplicationImpl(this, 69 shell_handle.Pass())); 70 apps_.push_back(app.release()); 71 } 72 73 virtual void OnApplicationError(ApplicationManager* manager, 74 const GURL& url) OVERRIDE {} 75 76 virtual bool ConfigureIncomingConnection(ApplicationConnection* connection) 77 OVERRIDE { 78 connection->AddService(view_manager_client_factory_.get()); 79 return true; 80 } 81 82 // Overridden from ViewManagerDelegate: 83 virtual void OnEmbed(ViewManager* view_manager, 84 View* root, 85 ServiceProviderImpl* exported_services, 86 scoped_ptr<ServiceProvider> imported_services) OVERRIDE { 87 callback_.Run(view_manager, root); 88 } 89 virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {} 90 91 ScopedVector<ApplicationImpl> apps_; 92 LoadedCallback callback_; 93 scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_; 94 95 DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader); 96}; 97 98class BoundsChangeObserver : public ViewObserver { 99 public: 100 explicit BoundsChangeObserver(View* view) : view_(view) {} 101 virtual ~BoundsChangeObserver() {} 102 103 private: 104 // Overridden from ViewObserver: 105 virtual void OnViewBoundsChanged(View* view, 106 const gfx::Rect& old_bounds, 107 const gfx::Rect& new_bounds) OVERRIDE { 108 DCHECK_EQ(view, view_); 109 QuitRunLoop(); 110 } 111 112 View* view_; 113 114 DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); 115}; 116 117// Wait until the bounds of the supplied view change. 118void WaitForBoundsToChange(View* view) { 119 BoundsChangeObserver observer(view); 120 view->AddObserver(&observer); 121 DoRunLoop(); 122 view->RemoveObserver(&observer); 123} 124 125// Spins a runloop until the tree beginning at |root| has |tree_size| views 126// (including |root|). 127class TreeSizeMatchesObserver : public ViewObserver { 128 public: 129 TreeSizeMatchesObserver(View* tree, size_t tree_size) 130 : tree_(tree), 131 tree_size_(tree_size) {} 132 virtual ~TreeSizeMatchesObserver() {} 133 134 bool IsTreeCorrectSize() { 135 return CountViews(tree_) == tree_size_; 136 } 137 138 private: 139 // Overridden from ViewObserver: 140 virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE { 141 if (IsTreeCorrectSize()) 142 QuitRunLoop(); 143 } 144 145 size_t CountViews(const View* view) const { 146 size_t count = 1; 147 View::Children::const_iterator it = view->children().begin(); 148 for (; it != view->children().end(); ++it) 149 count += CountViews(*it); 150 return count; 151 } 152 153 View* tree_; 154 size_t tree_size_; 155 156 DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver); 157}; 158 159void WaitForTreeSizeToMatch(View* view, size_t tree_size) { 160 TreeSizeMatchesObserver observer(view, tree_size); 161 if (observer.IsTreeCorrectSize()) 162 return; 163 view->AddObserver(&observer); 164 DoRunLoop(); 165 view->RemoveObserver(&observer); 166} 167 168// Utility class that waits for the destruction of some number of views and 169// views. 170class DestructionObserver : public ViewObserver { 171 public: 172 // |views| or |views| can be NULL. 173 explicit DestructionObserver(std::set<Id>* views) : views_(views) {} 174 175 private: 176 // Overridden from ViewObserver: 177 virtual void OnViewDestroyed(View* view) OVERRIDE { 178 std::set<Id>::iterator it = views_->find(view->id()); 179 if (it != views_->end()) 180 views_->erase(it); 181 if (CanQuit()) 182 QuitRunLoop(); 183 } 184 185 bool CanQuit() { 186 return !views_ || views_->empty(); 187 } 188 189 std::set<Id>* views_; 190 191 DISALLOW_COPY_AND_ASSIGN(DestructionObserver); 192}; 193 194void WaitForDestruction(ViewManager* view_manager, std::set<Id>* views) { 195 DestructionObserver observer(views); 196 DCHECK(views); 197 if (views) { 198 for (std::set<Id>::const_iterator it = views->begin(); 199 it != views->end(); ++it) { 200 view_manager->GetViewById(*it)->AddObserver(&observer); 201 } 202 } 203 DoRunLoop(); 204} 205 206class OrderChangeObserver : public ViewObserver { 207 public: 208 OrderChangeObserver(View* view) : view_(view) { 209 view_->AddObserver(this); 210 } 211 virtual ~OrderChangeObserver() { 212 view_->RemoveObserver(this); 213 } 214 215 private: 216 // Overridden from ViewObserver: 217 virtual void OnViewReordered(View* view, 218 View* relative_view, 219 OrderDirection direction) OVERRIDE { 220 DCHECK_EQ(view, view_); 221 QuitRunLoop(); 222 } 223 224 View* view_; 225 226 DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); 227}; 228 229void WaitForOrderChange(ViewManager* view_manager, View* view) { 230 OrderChangeObserver observer(view); 231 DoRunLoop(); 232} 233 234// Tracks a view's destruction. Query is_valid() for current state. 235class ViewTracker : public ViewObserver { 236 public: 237 explicit ViewTracker(View* view) : view_(view) { 238 view_->AddObserver(this); 239 } 240 virtual ~ViewTracker() { 241 if (view_) 242 view_->RemoveObserver(this); 243 } 244 245 bool is_valid() const { return !!view_; } 246 247 private: 248 // Overridden from ViewObserver: 249 virtual void OnViewDestroyed(View* view) OVERRIDE { 250 DCHECK_EQ(view, view_); 251 view_ = NULL; 252 } 253 254 int id_; 255 View* view_; 256 257 DISALLOW_COPY_AND_ASSIGN(ViewTracker); 258}; 259 260} // namespace 261 262// ViewManager ----------------------------------------------------------------- 263 264// These tests model synchronization of two peer connections to the view manager 265// service, that are given access to some root view. 266 267class ViewManagerTest : public testing::Test { 268 public: 269 ViewManagerTest() 270 : connect_loop_(NULL), 271 loaded_view_manager_(NULL), 272 window_manager_(NULL), 273 commit_count_(0) {} 274 275 protected: 276 ViewManager* window_manager() { return window_manager_; } 277 278 View* CreateViewInParent(View* parent) { 279 ViewManager* parent_manager = ViewPrivate(parent).view_manager(); 280 View* view = View::Create(parent_manager); 281 parent->AddChild(view); 282 return view; 283 } 284 285 // Embeds another version of the test app @ view. 286 ViewManager* Embed(ViewManager* view_manager, View* view) { 287 DCHECK_EQ(view_manager, ViewPrivate(view).view_manager()); 288 view->Embed(kEmbeddedApp1URL); 289 RunRunLoop(); 290 return GetLoadedViewManager(); 291 } 292 293 ViewManager* GetLoadedViewManager() { 294 ViewManager* view_manager = loaded_view_manager_; 295 loaded_view_manager_ = NULL; 296 return view_manager; 297 } 298 299 void UnloadApplication(const GURL& url) { 300 test_helper_.SetLoaderForURL(scoped_ptr<ApplicationLoader>(), url); 301 } 302 303 private: 304 // Overridden from testing::Test: 305 virtual void SetUp() OVERRIDE { 306 ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind( 307 &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this)); 308 test_helper_.Init(); 309 test_helper_.SetLoaderForURL( 310 scoped_ptr<ApplicationLoader>( 311 new ConnectApplicationLoader(ready_callback)), 312 GURL(kWindowManagerURL)); 313 test_helper_.SetLoaderForURL( 314 scoped_ptr<ApplicationLoader>( 315 new ConnectApplicationLoader(ready_callback)), 316 GURL(kEmbeddedApp1URL)); 317 318 test_helper_.application_manager()->ConnectToService( 319 GURL("mojo:mojo_view_manager"), &view_manager_init_); 320 ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kWindowManagerURL)); 321 } 322 323 void EmbedRootCallback(bool* result_cache, bool result) { 324 *result_cache = result; 325 } 326 327 bool EmbedRoot(ViewManagerInitService* view_manager_init, 328 const std::string& url) { 329 bool result = false; 330 ServiceProviderPtr sp; 331 BindToProxy(new ServiceProviderImpl, &sp); 332 view_manager_init->Embed( 333 url, sp.Pass(), 334 base::Bind(&ViewManagerTest::EmbedRootCallback, base::Unretained(this), 335 &result)); 336 RunRunLoop(); 337 window_manager_ = GetLoadedViewManager(); 338 return result; 339 } 340 341 void OnViewManagerLoaded(ViewManager* view_manager, View* root) { 342 loaded_view_manager_ = view_manager; 343 connect_loop_->Quit(); 344 } 345 346 void RunRunLoop() { 347 base::RunLoop run_loop; 348 connect_loop_ = &run_loop; 349 connect_loop_->Run(); 350 connect_loop_ = NULL; 351 } 352 353 base::RunLoop* connect_loop_; 354 shell::ShellTestHelper test_helper_; 355 ViewManagerInitServicePtr view_manager_init_; 356 // Used to receive the most recent view manager loaded by an embed action. 357 ViewManager* loaded_view_manager_; 358 // The View Manager connection held by the window manager (app running at the 359 // root view). 360 ViewManager* window_manager_; 361 int commit_count_; 362 363 DISALLOW_COPY_AND_ASSIGN(ViewManagerTest); 364}; 365 366TEST_F(ViewManagerTest, SetUp) {} 367 368TEST_F(ViewManagerTest, Embed) { 369 View* view = View::Create(window_manager()); 370 window_manager()->GetRoots().front()->AddChild(view); 371 ViewManager* embedded = Embed(window_manager(), view); 372 EXPECT_TRUE(NULL != embedded); 373 374 View* view_in_embedded = embedded->GetRoots().front(); 375 EXPECT_EQ(view->parent(), window_manager()->GetRoots().front()); 376 EXPECT_EQ(NULL, view_in_embedded->parent()); 377} 378 379// Window manager has two views, N1 and N11. Embeds A at N1. A should not see 380// N11. 381// TODO(sky): Update client lib to match server. 382TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) { 383 View* view = View::Create(window_manager()); 384 window_manager()->GetRoots().front()->AddChild(view); 385 View* nested = View::Create(window_manager()); 386 view->AddChild(nested); 387 388 ViewManager* embedded = Embed(window_manager(), view); 389 EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(), 390 nested->id()); 391 EXPECT_TRUE(embedded->GetRoots().front()->children().empty()); 392 EXPECT_TRUE(nested->parent() == NULL); 393} 394 395// http://crbug.com/396300 396TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) { 397 View* view = View::Create(window_manager()); 398 window_manager()->GetRoots().front()->AddChild(view); 399 ViewManager* embedded = Embed(window_manager(), view); 400 401 Id view_id = view->id(); 402 403 UnloadApplication(GURL(kWindowManagerURL)); 404 405 std::set<Id> views; 406 views.insert(view_id); 407 WaitForDestruction(embedded, &views); 408 409 EXPECT_TRUE(embedded->GetRoots().empty()); 410} 411 412// TODO(beng): write a replacement test for the one that once existed here: 413// This test validates the following scenario: 414// - a view originating from one connection 415// - a view originating from a second connection 416// + the connection originating the view is destroyed 417// -> the view should still exist (since the second connection is live) but 418// should be disconnected from any views. 419// http://crbug.com/396300 420// 421// TODO(beng): The new test should validate the scenario as described above 422// except that the second connection still has a valid tree. 423 424// Verifies that bounds changes applied to a view hierarchy in one connection 425// are reflected to another. 426TEST_F(ViewManagerTest, SetBounds) { 427 View* view = View::Create(window_manager()); 428 window_manager()->GetRoots().front()->AddChild(view); 429 ViewManager* embedded = Embed(window_manager(), view); 430 431 View* view_in_embedded = embedded->GetViewById(view->id()); 432 EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); 433 434 view->SetBounds(gfx::Rect(100, 100)); 435 EXPECT_NE(view->bounds(), view_in_embedded->bounds()); 436 WaitForBoundsToChange(view_in_embedded); 437 EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); 438} 439 440// Verifies that bounds changes applied to a view owned by a different 441// connection are refused. 442TEST_F(ViewManagerTest, SetBoundsSecurity) { 443 View* view = View::Create(window_manager()); 444 window_manager()->GetRoots().front()->AddChild(view); 445 ViewManager* embedded = Embed(window_manager(), view); 446 447 View* view_in_embedded = embedded->GetViewById(view->id()); 448 view->SetBounds(gfx::Rect(800, 600)); 449 WaitForBoundsToChange(view_in_embedded); 450 451 view_in_embedded->SetBounds(gfx::Rect(1024, 768)); 452 // Bounds change should have been rejected. 453 EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); 454} 455 456// Verifies that a view can only be destroyed by the connection that created it. 457TEST_F(ViewManagerTest, DestroySecurity) { 458 View* view = View::Create(window_manager()); 459 window_manager()->GetRoots().front()->AddChild(view); 460 ViewManager* embedded = Embed(window_manager(), view); 461 462 View* view_in_embedded = embedded->GetViewById(view->id()); 463 464 ViewTracker tracker2(view_in_embedded); 465 view_in_embedded->Destroy(); 466 // View should not have been destroyed. 467 EXPECT_TRUE(tracker2.is_valid()); 468 469 ViewTracker tracker1(view); 470 view->Destroy(); 471 EXPECT_FALSE(tracker1.is_valid()); 472} 473 474TEST_F(ViewManagerTest, MultiRoots) { 475 View* view1 = View::Create(window_manager()); 476 window_manager()->GetRoots().front()->AddChild(view1); 477 View* view2 = View::Create(window_manager()); 478 window_manager()->GetRoots().front()->AddChild(view2); 479 ViewManager* embedded1 = Embed(window_manager(), view1); 480 ViewManager* embedded2 = Embed(window_manager(), view2); 481 EXPECT_EQ(embedded1, embedded2); 482} 483 484TEST_F(ViewManagerTest, EmbeddingIdentity) { 485 View* view = View::Create(window_manager()); 486 window_manager()->GetRoots().front()->AddChild(view); 487 ViewManager* embedded = Embed(window_manager(), view); 488 EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL()); 489} 490 491TEST_F(ViewManagerTest, Reorder) { 492 View* view1 = View::Create(window_manager()); 493 window_manager()->GetRoots().front()->AddChild(view1); 494 495 ViewManager* embedded = Embed(window_manager(), view1); 496 497 View* view11 = View::Create(embedded); 498 embedded->GetRoots().front()->AddChild(view11); 499 View* view12 = View::Create(embedded); 500 embedded->GetRoots().front()->AddChild(view12); 501 502 View* view1_in_wm = window_manager()->GetViewById(view1->id()); 503 504 { 505 WaitForTreeSizeToMatch(view1, 2u); 506 view11->MoveToFront(); 507 WaitForOrderChange(window_manager(), 508 window_manager()->GetViewById(view11->id())); 509 510 EXPECT_EQ(view1_in_wm->children().front(), 511 window_manager()->GetViewById(view12->id())); 512 EXPECT_EQ(view1_in_wm->children().back(), 513 window_manager()->GetViewById(view11->id())); 514 } 515 516 { 517 view11->MoveToBack(); 518 WaitForOrderChange(window_manager(), 519 window_manager()->GetViewById(view11->id())); 520 521 EXPECT_EQ(view1_in_wm->children().front(), 522 window_manager()->GetViewById(view11->id())); 523 EXPECT_EQ(view1_in_wm->children().back(), 524 window_manager()->GetViewById(view12->id())); 525 } 526} 527 528// TODO(beng): tests for view event dispatcher. 529// - verify that we see events for all views. 530 531// TODO(beng): tests for focus: 532// - focus between two views known to a connection 533// - focus between views unknown to one of the connections. 534// - focus between views unknown to either connection. 535 536} // namespace mojo 537