| OLD | NEW |
| (Empty) |
| 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 <stddef.h> | |
| 6 #include <stdint.h> | |
| 7 | |
| 8 #include "base/bind.h" | |
| 9 #include "base/macros.h" | |
| 10 #include "base/message_loop/message_loop.h" | |
| 11 #include "base/run_loop.h" | |
| 12 #include "base/strings/stringprintf.h" | |
| 13 #include "components/mus/public/interfaces/window_tree.mojom.h" | |
| 14 #include "components/mus/public/interfaces/window_tree_host.mojom.h" | |
| 15 #include "components/mus/ws/ids.h" | |
| 16 #include "components/mus/ws/test_change_tracker.h" | |
| 17 #include "mojo/converters/geometry/geometry_type_converters.h" | |
| 18 #include "mojo/public/cpp/bindings/associated_binding.h" | |
| 19 #include "mojo/shell/public/cpp/application_test_base.h" | |
| 20 | |
| 21 using mojo::Array; | |
| 22 using mojo::Callback; | |
| 23 using mojo::Connection; | |
| 24 using mojo::InterfaceRequest; | |
| 25 using mojo::RectPtr; | |
| 26 using mojo::ShellClient; | |
| 27 using mojo::String; | |
| 28 using mus::mojom::ErrorCode; | |
| 29 using mus::mojom::EventPtr; | |
| 30 using mus::mojom::ViewportMetricsPtr; | |
| 31 using mus::mojom::WindowDataPtr; | |
| 32 using mus::mojom::WindowTree; | |
| 33 using mus::mojom::WindowTreeClient; | |
| 34 | |
| 35 namespace mus { | |
| 36 namespace ws { | |
| 37 namespace test { | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 // Creates an id used for transport from the specified parameters. | |
| 42 Id BuildWindowId(ConnectionSpecificId connection_id, | |
| 43 ConnectionSpecificId window_id) { | |
| 44 return (connection_id << 16) | window_id; | |
| 45 } | |
| 46 | |
| 47 // Callback function from WindowTree functions. | |
| 48 // ---------------------------------- | |
| 49 | |
| 50 void WindowTreeResultCallback(base::RunLoop* run_loop, | |
| 51 std::vector<TestWindow>* windows, | |
| 52 Array<WindowDataPtr> results) { | |
| 53 WindowDatasToTestWindows(results, windows); | |
| 54 run_loop->Quit(); | |
| 55 } | |
| 56 | |
| 57 void EmbedCallbackImpl(base::RunLoop* run_loop, | |
| 58 bool* result_cache, | |
| 59 bool result, | |
| 60 ConnectionSpecificId connection_id) { | |
| 61 *result_cache = result; | |
| 62 run_loop->Quit(); | |
| 63 } | |
| 64 | |
| 65 // ----------------------------------------------------------------------------- | |
| 66 | |
| 67 bool EmbedUrl(mojo::Connector* connector, | |
| 68 WindowTree* tree, | |
| 69 const String& url, | |
| 70 Id root_id) { | |
| 71 bool result = false; | |
| 72 base::RunLoop run_loop; | |
| 73 { | |
| 74 mojom::WindowTreeClientPtr client; | |
| 75 connector->ConnectToInterface(url.get(), &client); | |
| 76 tree->Embed(root_id, std::move(client), | |
| 77 mojom::WindowTree::kAccessPolicyDefault, | |
| 78 base::Bind(&EmbedCallbackImpl, &run_loop, &result)); | |
| 79 } | |
| 80 run_loop.Run(); | |
| 81 return result; | |
| 82 } | |
| 83 | |
| 84 bool Embed(WindowTree* tree, Id root_id, mojom::WindowTreeClientPtr client) { | |
| 85 bool result = false; | |
| 86 base::RunLoop run_loop; | |
| 87 { | |
| 88 tree->Embed(root_id, std::move(client), | |
| 89 mojom::WindowTree::kAccessPolicyDefault, | |
| 90 base::Bind(&EmbedCallbackImpl, &run_loop, &result)); | |
| 91 } | |
| 92 run_loop.Run(); | |
| 93 return result; | |
| 94 } | |
| 95 | |
| 96 void GetWindowTree(WindowTree* tree, | |
| 97 Id window_id, | |
| 98 std::vector<TestWindow>* windows) { | |
| 99 base::RunLoop run_loop; | |
| 100 tree->GetWindowTree( | |
| 101 window_id, base::Bind(&WindowTreeResultCallback, &run_loop, windows)); | |
| 102 run_loop.Run(); | |
| 103 } | |
| 104 | |
| 105 // Utility functions ----------------------------------------------------------- | |
| 106 | |
| 107 const Id kNullParentId = 0; | |
| 108 std::string IdToString(Id id) { | |
| 109 return (id == kNullParentId) ? "null" : base::StringPrintf( | |
| 110 "%d,%d", HiWord(id), LoWord(id)); | |
| 111 } | |
| 112 | |
| 113 std::string WindowParentToString(Id window, Id parent) { | |
| 114 return base::StringPrintf("window=%s parent=%s", IdToString(window).c_str(), | |
| 115 IdToString(parent).c_str()); | |
| 116 } | |
| 117 | |
| 118 // ----------------------------------------------------------------------------- | |
| 119 | |
| 120 // A WindowTreeClient implementation that logs all changes to a tracker. | |
| 121 class TestWindowTreeClientImpl : public mojom::WindowTreeClient, | |
| 122 public TestChangeTracker::Delegate, | |
| 123 public mojom::WindowManager { | |
| 124 public: | |
| 125 TestWindowTreeClientImpl() | |
| 126 : binding_(this), | |
| 127 connection_id_(0), | |
| 128 root_window_id_(0), | |
| 129 // Start with a random large number so tests can use lower ids if they | |
| 130 // want. | |
| 131 next_change_id_(10000), | |
| 132 waiting_change_id_(0), | |
| 133 on_change_completed_result_(false), | |
| 134 track_root_bounds_changes_(false) { | |
| 135 tracker_.set_delegate(this); | |
| 136 } | |
| 137 | |
| 138 void Bind(mojo::InterfaceRequest<mojom::WindowTreeClient> request) { | |
| 139 binding_.Bind(std::move(request)); | |
| 140 } | |
| 141 | |
| 142 mojom::WindowTree* tree() { return tree_.get(); } | |
| 143 TestChangeTracker* tracker() { return &tracker_; } | |
| 144 Id root_window_id() const { return root_window_id_; } | |
| 145 | |
| 146 // Sets whether changes to the bounds of the root should be tracked. Normally | |
| 147 // they are ignored (as during startup we often times get random size | |
| 148 // changes). | |
| 149 void set_track_root_bounds_changes(bool value) { | |
| 150 track_root_bounds_changes_ = value; | |
| 151 } | |
| 152 | |
| 153 // Runs a nested MessageLoop until |count| changes (calls to | |
| 154 // WindowTreeClient functions) have been received. | |
| 155 void WaitForChangeCount(size_t count) { | |
| 156 if (tracker_.changes()->size() >= count) | |
| 157 return; | |
| 158 | |
| 159 ASSERT_TRUE(wait_state_.get() == nullptr); | |
| 160 wait_state_.reset(new WaitState); | |
| 161 wait_state_->change_count = count; | |
| 162 wait_state_->run_loop.Run(); | |
| 163 wait_state_.reset(); | |
| 164 } | |
| 165 | |
| 166 uint32_t GetAndAdvanceChangeId() { return next_change_id_++; } | |
| 167 | |
| 168 // Runs a nested MessageLoop until OnEmbed() has been encountered. | |
| 169 void WaitForOnEmbed() { | |
| 170 if (tree_) | |
| 171 return; | |
| 172 embed_run_loop_.reset(new base::RunLoop); | |
| 173 embed_run_loop_->Run(); | |
| 174 embed_run_loop_.reset(); | |
| 175 } | |
| 176 | |
| 177 bool WaitForIncomingMethodCall() { | |
| 178 return binding_.WaitForIncomingMethodCall(); | |
| 179 } | |
| 180 | |
| 181 bool WaitForChangeCompleted(uint32_t id) { | |
| 182 waiting_change_id_ = id; | |
| 183 change_completed_run_loop_.reset(new base::RunLoop); | |
| 184 change_completed_run_loop_->Run(); | |
| 185 return on_change_completed_result_; | |
| 186 } | |
| 187 | |
| 188 bool DeleteWindow(Id id) { | |
| 189 const uint32_t change_id = GetAndAdvanceChangeId(); | |
| 190 tree()->DeleteWindow(change_id, id); | |
| 191 return WaitForChangeCompleted(change_id); | |
| 192 } | |
| 193 | |
| 194 bool AddWindow(Id parent, Id child) { | |
| 195 const uint32_t change_id = GetAndAdvanceChangeId(); | |
| 196 tree()->AddWindow(change_id, parent, child); | |
| 197 return WaitForChangeCompleted(change_id); | |
| 198 } | |
| 199 | |
| 200 bool RemoveWindowFromParent(Id window_id) { | |
| 201 const uint32_t change_id = GetAndAdvanceChangeId(); | |
| 202 tree()->RemoveWindowFromParent(change_id, window_id); | |
| 203 return WaitForChangeCompleted(change_id); | |
| 204 } | |
| 205 | |
| 206 bool ReorderWindow(Id window_id, | |
| 207 Id relative_window_id, | |
| 208 mojom::OrderDirection direction) { | |
| 209 const uint32_t change_id = GetAndAdvanceChangeId(); | |
| 210 tree()->ReorderWindow(change_id, window_id, relative_window_id, direction); | |
| 211 return WaitForChangeCompleted(change_id); | |
| 212 } | |
| 213 | |
| 214 // Waits for all messages to be received by |ws|. This is done by attempting | |
| 215 // to create a bogus window. When we get the response we know all messages | |
| 216 // have been processed. | |
| 217 bool WaitForAllMessages() { | |
| 218 return NewWindowWithCompleteId(WindowIdToTransportId(InvalidWindowId())) == | |
| 219 0; | |
| 220 } | |
| 221 | |
| 222 Id NewWindow(ConnectionSpecificId window_id) { | |
| 223 return NewWindowWithCompleteId(BuildWindowId(connection_id_, window_id)); | |
| 224 } | |
| 225 | |
| 226 // Generally you want NewWindow(), but use this if you need to test given | |
| 227 // a complete window id (NewWindow() ors with the connection id). | |
| 228 Id NewWindowWithCompleteId(Id id) { | |
| 229 mojo::Map<mojo::String, mojo::Array<uint8_t>> properties; | |
| 230 const uint32_t change_id = GetAndAdvanceChangeId(); | |
| 231 tree()->NewWindow(change_id, id, std::move(properties)); | |
| 232 return WaitForChangeCompleted(change_id) ? id : 0; | |
| 233 } | |
| 234 | |
| 235 bool SetWindowProperty(Id window_id, | |
| 236 const std::string& name, | |
| 237 const std::vector<uint8_t>* data) { | |
| 238 Array<uint8_t> mojo_data(nullptr); | |
| 239 if (data) | |
| 240 mojo_data = Array<uint8_t>::From(*data); | |
| 241 const uint32_t change_id = GetAndAdvanceChangeId(); | |
| 242 tree()->SetWindowProperty(change_id, window_id, name, std::move(mojo_data)); | |
| 243 return WaitForChangeCompleted(change_id); | |
| 244 } | |
| 245 | |
| 246 bool SetPredefinedCursor(Id window_id, mojom::Cursor cursor) { | |
| 247 const uint32_t change_id = GetAndAdvanceChangeId(); | |
| 248 tree()->SetPredefinedCursor(change_id, window_id, cursor); | |
| 249 return WaitForChangeCompleted(change_id); | |
| 250 } | |
| 251 | |
| 252 bool SetWindowVisibility(Id window_id, bool visible) { | |
| 253 const uint32_t change_id = GetAndAdvanceChangeId(); | |
| 254 tree()->SetWindowVisibility(change_id, window_id, visible); | |
| 255 return WaitForChangeCompleted(change_id); | |
| 256 } | |
| 257 | |
| 258 private: | |
| 259 // Used when running a nested MessageLoop. | |
| 260 struct WaitState { | |
| 261 WaitState() : change_count(0) {} | |
| 262 | |
| 263 // Number of changes waiting for. | |
| 264 size_t change_count; | |
| 265 base::RunLoop run_loop; | |
| 266 }; | |
| 267 | |
| 268 // TestChangeTracker::Delegate: | |
| 269 void OnChangeAdded() override { | |
| 270 if (wait_state_.get() && | |
| 271 tracker_.changes()->size() >= wait_state_->change_count) { | |
| 272 wait_state_->run_loop.Quit(); | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 // WindowTreeClient: | |
| 277 void OnEmbed(ConnectionSpecificId connection_id, | |
| 278 WindowDataPtr root, | |
| 279 mojom::WindowTreePtr tree, | |
| 280 Id focused_window_id, | |
| 281 uint32_t access_policy) override { | |
| 282 // TODO(sky): add coverage of |focused_window_id|. | |
| 283 ASSERT_TRUE(root); | |
| 284 root_window_id_ = root->window_id; | |
| 285 tree_ = std::move(tree); | |
| 286 connection_id_ = connection_id; | |
| 287 tracker()->OnEmbed(connection_id, std::move(root)); | |
| 288 if (embed_run_loop_) | |
| 289 embed_run_loop_->Quit(); | |
| 290 } | |
| 291 void OnEmbeddedAppDisconnected(Id window_id) override { | |
| 292 tracker()->OnEmbeddedAppDisconnected(window_id); | |
| 293 } | |
| 294 void OnUnembed(Id window_id) override { tracker()->OnUnembed(window_id); } | |
| 295 void OnLostCapture(Id window_id) override { | |
| 296 tracker()->OnLostCapture(window_id); | |
| 297 } | |
| 298 void OnTopLevelCreated(uint32_t change_id, | |
| 299 mojom::WindowDataPtr data) override { | |
| 300 tracker()->OnTopLevelCreated(change_id, std::move(data)); | |
| 301 } | |
| 302 void OnWindowBoundsChanged(Id window_id, | |
| 303 RectPtr old_bounds, | |
| 304 RectPtr new_bounds) override { | |
| 305 // The bounds of the root may change during startup on Android at random | |
| 306 // times. As this doesn't matter, and shouldn't impact test exepctations, | |
| 307 // it is ignored. | |
| 308 if (window_id == root_window_id_ && !track_root_bounds_changes_) | |
| 309 return; | |
| 310 tracker()->OnWindowBoundsChanged(window_id, std::move(old_bounds), | |
| 311 std::move(new_bounds)); | |
| 312 } | |
| 313 void OnClientAreaChanged( | |
| 314 uint32_t window_id, | |
| 315 mojo::InsetsPtr new_client_area, | |
| 316 mojo::Array<mojo::RectPtr> new_additional_client_areas) override {} | |
| 317 void OnTransientWindowAdded(uint32_t window_id, | |
| 318 uint32_t transient_window_id) override { | |
| 319 tracker()->OnTransientWindowAdded(window_id, transient_window_id); | |
| 320 } | |
| 321 void OnTransientWindowRemoved(uint32_t window_id, | |
| 322 uint32_t transient_window_id) override { | |
| 323 tracker()->OnTransientWindowRemoved(window_id, transient_window_id); | |
| 324 } | |
| 325 void OnWindowViewportMetricsChanged(mojo::Array<uint32_t> window_ids, | |
| 326 ViewportMetricsPtr old_metrics, | |
| 327 ViewportMetricsPtr new_metrics) override { | |
| 328 // Don't track the metrics as they are available at an indeterministic time | |
| 329 // on Android. | |
| 330 } | |
| 331 void OnWindowHierarchyChanged(Id window, | |
| 332 Id new_parent, | |
| 333 Id old_parent, | |
| 334 Array<WindowDataPtr> windows) override { | |
| 335 tracker()->OnWindowHierarchyChanged(window, new_parent, old_parent, | |
| 336 std::move(windows)); | |
| 337 } | |
| 338 void OnWindowReordered(Id window_id, | |
| 339 Id relative_window_id, | |
| 340 mojom::OrderDirection direction) override { | |
| 341 tracker()->OnWindowReordered(window_id, relative_window_id, direction); | |
| 342 } | |
| 343 void OnWindowDeleted(Id window) override { | |
| 344 tracker()->OnWindowDeleted(window); | |
| 345 } | |
| 346 void OnWindowVisibilityChanged(uint32_t window, bool visible) override { | |
| 347 tracker()->OnWindowVisibilityChanged(window, visible); | |
| 348 } | |
| 349 void OnWindowDrawnStateChanged(uint32_t window, bool drawn) override { | |
| 350 tracker()->OnWindowDrawnStateChanged(window, drawn); | |
| 351 } | |
| 352 void OnWindowInputEvent(uint32_t event_id, | |
| 353 Id window_id, | |
| 354 EventPtr event) override { | |
| 355 // Ack input events to clear the state on the server. These can be received | |
| 356 // during test startup. X11Window::DispatchEvent sends a synthetic move | |
| 357 // event to notify of entry. | |
| 358 tree()->OnWindowInputEventAck(event_id); | |
| 359 // Don't log input events as none of the tests care about them and they | |
| 360 // may come in at random points. | |
| 361 } | |
| 362 void OnWindowSharedPropertyChanged(uint32_t window, | |
| 363 const String& name, | |
| 364 Array<uint8_t> new_data) override { | |
| 365 tracker_.OnWindowSharedPropertyChanged(window, name, std::move(new_data)); | |
| 366 } | |
| 367 // TODO(sky): add testing coverage. | |
| 368 void OnWindowFocused(uint32_t focused_window_id) override {} | |
| 369 void OnWindowPredefinedCursorChanged(uint32_t window_id, | |
| 370 mojom::Cursor cursor_id) override { | |
| 371 tracker_.OnWindowPredefinedCursorChanged(window_id, cursor_id); | |
| 372 } | |
| 373 void OnChangeCompleted(uint32_t change_id, bool success) override { | |
| 374 if (waiting_change_id_ == change_id && change_completed_run_loop_) { | |
| 375 on_change_completed_result_ = success; | |
| 376 change_completed_run_loop_->Quit(); | |
| 377 } | |
| 378 } | |
| 379 void RequestClose(uint32_t window_id) override {} | |
| 380 void GetWindowManager(mojo::AssociatedInterfaceRequest<mojom::WindowManager> | |
| 381 internal) override { | |
| 382 window_manager_binding_.reset( | |
| 383 new mojo::AssociatedBinding<mojom::WindowManager>(this, | |
| 384 std::move(internal))); | |
| 385 tree_->GetWindowManagerClient( | |
| 386 GetProxy(&window_manager_client_, tree_.associated_group())); | |
| 387 } | |
| 388 | |
| 389 // mojom::WindowManager: | |
| 390 void WmSetBounds(uint32_t change_id, | |
| 391 uint32_t window_id, | |
| 392 mojo::RectPtr bounds) override { | |
| 393 window_manager_client_->WmResponse(change_id, false); | |
| 394 } | |
| 395 void WmSetProperty(uint32_t change_id, | |
| 396 uint32_t window_id, | |
| 397 const mojo::String& name, | |
| 398 mojo::Array<uint8_t> value) override { | |
| 399 window_manager_client_->WmResponse(change_id, false); | |
| 400 } | |
| 401 void WmCreateTopLevelWindow( | |
| 402 uint32_t change_id, | |
| 403 mojo::Map<mojo::String, mojo::Array<uint8_t>> properties) override { | |
| 404 NOTIMPLEMENTED(); | |
| 405 } | |
| 406 void OnAccelerator(uint32_t id, mojom::EventPtr event) override { | |
| 407 NOTIMPLEMENTED(); | |
| 408 } | |
| 409 | |
| 410 TestChangeTracker tracker_; | |
| 411 | |
| 412 mojom::WindowTreePtr tree_; | |
| 413 | |
| 414 // If non-null we're waiting for OnEmbed() using this RunLoop. | |
| 415 scoped_ptr<base::RunLoop> embed_run_loop_; | |
| 416 | |
| 417 // If non-null we're waiting for a certain number of change notifications to | |
| 418 // be encountered. | |
| 419 scoped_ptr<WaitState> wait_state_; | |
| 420 | |
| 421 mojo::Binding<WindowTreeClient> binding_; | |
| 422 Id connection_id_; | |
| 423 Id root_window_id_; | |
| 424 uint32_t next_change_id_; | |
| 425 uint32_t waiting_change_id_; | |
| 426 bool on_change_completed_result_; | |
| 427 bool track_root_bounds_changes_; | |
| 428 scoped_ptr<base::RunLoop> change_completed_run_loop_; | |
| 429 | |
| 430 scoped_ptr<mojo::AssociatedBinding<mojom::WindowManager>> | |
| 431 window_manager_binding_; | |
| 432 mojom::WindowManagerClientAssociatedPtr window_manager_client_; | |
| 433 | |
| 434 DISALLOW_COPY_AND_ASSIGN(TestWindowTreeClientImpl); | |
| 435 }; | |
| 436 | |
| 437 // ----------------------------------------------------------------------------- | |
| 438 | |
| 439 // InterfaceFactory for vending TestWindowTreeClientImpls. | |
| 440 class WindowTreeClientFactory | |
| 441 : public mojo::InterfaceFactory<WindowTreeClient> { | |
| 442 public: | |
| 443 WindowTreeClientFactory() {} | |
| 444 ~WindowTreeClientFactory() override {} | |
| 445 | |
| 446 // Runs a nested MessageLoop until a new instance has been created. | |
| 447 scoped_ptr<TestWindowTreeClientImpl> WaitForInstance() { | |
| 448 if (!client_impl_.get()) { | |
| 449 DCHECK(!run_loop_); | |
| 450 run_loop_.reset(new base::RunLoop); | |
| 451 run_loop_->Run(); | |
| 452 run_loop_.reset(); | |
| 453 } | |
| 454 return std::move(client_impl_); | |
| 455 } | |
| 456 | |
| 457 private: | |
| 458 // InterfaceFactory<WindowTreeClient>: | |
| 459 void Create(Connection* connection, | |
| 460 InterfaceRequest<WindowTreeClient> request) override { | |
| 461 client_impl_.reset(new TestWindowTreeClientImpl()); | |
| 462 client_impl_->Bind(std::move(request)); | |
| 463 if (run_loop_.get()) | |
| 464 run_loop_->Quit(); | |
| 465 } | |
| 466 | |
| 467 scoped_ptr<TestWindowTreeClientImpl> client_impl_; | |
| 468 scoped_ptr<base::RunLoop> run_loop_; | |
| 469 | |
| 470 DISALLOW_COPY_AND_ASSIGN(WindowTreeClientFactory); | |
| 471 }; | |
| 472 | |
| 473 } // namespace | |
| 474 | |
| 475 class WindowTreeAppTest : public mojo::test::ApplicationTestBase, | |
| 476 public mojo::ShellClient { | |
| 477 public: | |
| 478 WindowTreeAppTest() | |
| 479 : connection_id_1_(0), connection_id_2_(0), root_window_id_(0) {} | |
| 480 ~WindowTreeAppTest() override {} | |
| 481 | |
| 482 protected: | |
| 483 // Returns the changes from the various connections. | |
| 484 std::vector<Change>* changes1() { return wt_client1_->tracker()->changes(); } | |
| 485 std::vector<Change>* changes2() { return wt_client2_->tracker()->changes(); } | |
| 486 std::vector<Change>* changes3() { return wt_client3_->tracker()->changes(); } | |
| 487 | |
| 488 // Various connections. |wt1()|, being the first connection, has special | |
| 489 // permissions (it's treated as the window manager). | |
| 490 WindowTree* wt1() { return wt_client1_->tree(); } | |
| 491 WindowTree* wt2() { return wt_client2_->tree(); } | |
| 492 WindowTree* wt3() { return wt_client3_->tree(); } | |
| 493 | |
| 494 TestWindowTreeClientImpl* wt_client1() { return wt_client1_.get(); } | |
| 495 TestWindowTreeClientImpl* wt_client2() { return wt_client2_.get(); } | |
| 496 TestWindowTreeClientImpl* wt_client3() { return wt_client3_.get(); } | |
| 497 | |
| 498 Id root_window_id() const { return root_window_id_; } | |
| 499 | |
| 500 int connection_id_1() const { return connection_id_1_; } | |
| 501 int connection_id_2() const { return connection_id_2_; } | |
| 502 | |
| 503 void EstablishSecondConnectionWithRoot(Id root_id) { | |
| 504 ASSERT_TRUE(wt_client2_.get() == nullptr); | |
| 505 wt_client2_ = | |
| 506 EstablishConnectionViaEmbed(wt1(), root_id, &connection_id_2_); | |
| 507 ASSERT_GT(connection_id_2_, 0); | |
| 508 ASSERT_TRUE(wt_client2_.get() != nullptr); | |
| 509 } | |
| 510 | |
| 511 void EstablishSecondConnection(bool create_initial_window) { | |
| 512 Id window_1_1 = 0; | |
| 513 if (create_initial_window) { | |
| 514 window_1_1 = wt_client1()->NewWindow(1); | |
| 515 ASSERT_TRUE(window_1_1); | |
| 516 } | |
| 517 ASSERT_NO_FATAL_FAILURE( | |
| 518 EstablishSecondConnectionWithRoot(BuildWindowId(connection_id_1(), 1))); | |
| 519 | |
| 520 if (create_initial_window) { | |
| 521 EXPECT_EQ("[" + WindowParentToString(window_1_1, kNullParentId) + "]", | |
| 522 ChangeWindowDescription(*changes2())); | |
| 523 } | |
| 524 } | |
| 525 | |
| 526 void EstablishThirdConnection(WindowTree* owner, Id root_id) { | |
| 527 ASSERT_TRUE(wt_client3_.get() == nullptr); | |
| 528 wt_client3_ = EstablishConnectionViaEmbed(owner, root_id, nullptr); | |
| 529 ASSERT_TRUE(wt_client3_.get() != nullptr); | |
| 530 } | |
| 531 | |
| 532 scoped_ptr<TestWindowTreeClientImpl> WaitForWindowTreeClient() { | |
| 533 return client_factory_->WaitForInstance(); | |
| 534 } | |
| 535 | |
| 536 // Establishes a new connection by way of Embed() on the specified | |
| 537 // WindowTree. | |
| 538 scoped_ptr<TestWindowTreeClientImpl> EstablishConnectionViaEmbed( | |
| 539 WindowTree* owner, | |
| 540 Id root_id, | |
| 541 int* connection_id) { | |
| 542 return EstablishConnectionViaEmbedWithPolicyBitmask( | |
| 543 owner, root_id, mojom::WindowTree::kAccessPolicyDefault, connection_id); | |
| 544 } | |
| 545 | |
| 546 scoped_ptr<TestWindowTreeClientImpl> | |
| 547 EstablishConnectionViaEmbedWithPolicyBitmask(WindowTree* owner, | |
| 548 Id root_id, | |
| 549 uint32_t policy_bitmask, | |
| 550 int* connection_id) { | |
| 551 if (!EmbedUrl(connector(), owner, test_name(), root_id)) { | |
| 552 ADD_FAILURE() << "Embed() failed"; | |
| 553 return nullptr; | |
| 554 } | |
| 555 scoped_ptr<TestWindowTreeClientImpl> client = | |
| 556 client_factory_->WaitForInstance(); | |
| 557 if (!client.get()) { | |
| 558 ADD_FAILURE() << "WaitForInstance failed"; | |
| 559 return nullptr; | |
| 560 } | |
| 561 client->WaitForOnEmbed(); | |
| 562 | |
| 563 EXPECT_EQ("OnEmbed", | |
| 564 SingleChangeToDescription(*client->tracker()->changes())); | |
| 565 if (connection_id) | |
| 566 *connection_id = (*client->tracker()->changes())[0].connection_id; | |
| 567 return client; | |
| 568 } | |
| 569 | |
| 570 // ApplicationTestBase: | |
| 571 mojo::ShellClient* GetShellClient() override { return this; } | |
| 572 void SetUp() override { | |
| 573 ApplicationTestBase::SetUp(); | |
| 574 client_factory_.reset(new WindowTreeClientFactory()); | |
| 575 | |
| 576 mojom::WindowTreeHostFactoryPtr factory; | |
| 577 connector()->ConnectToInterface("mojo:mus", &factory); | |
| 578 | |
| 579 mojom::WindowTreeClientPtr tree_client_ptr; | |
| 580 wt_client1_.reset(new TestWindowTreeClientImpl()); | |
| 581 wt_client1_->Bind(GetProxy(&tree_client_ptr)); | |
| 582 | |
| 583 factory->CreateWindowTreeHost(GetProxy(&host_), | |
| 584 std::move(tree_client_ptr)); | |
| 585 | |
| 586 // Next we should get an embed call on the "window manager" client. | |
| 587 wt_client1_->WaitForIncomingMethodCall(); | |
| 588 | |
| 589 ASSERT_EQ(1u, changes1()->size()); | |
| 590 EXPECT_EQ(CHANGE_TYPE_EMBED, (*changes1())[0].type); | |
| 591 // All these tests assume 1 for the client id. The only real assertion here | |
| 592 // is the client id is not zero, but adding this as rest of code here | |
| 593 // assumes 1. | |
| 594 ASSERT_GT((*changes1())[0].connection_id, 0); | |
| 595 connection_id_1_ = (*changes1())[0].connection_id; | |
| 596 ASSERT_FALSE((*changes1())[0].windows.empty()); | |
| 597 root_window_id_ = (*changes1())[0].windows[0].window_id; | |
| 598 ASSERT_EQ(root_window_id_, wt_client1_->root_window_id()); | |
| 599 changes1()->clear(); | |
| 600 } | |
| 601 | |
| 602 // mojo::ShellClient implementation. | |
| 603 bool AcceptConnection(Connection* connection) override { | |
| 604 connection->AddInterface(client_factory_.get()); | |
| 605 return true; | |
| 606 } | |
| 607 | |
| 608 scoped_ptr<TestWindowTreeClientImpl> wt_client1_; | |
| 609 scoped_ptr<TestWindowTreeClientImpl> wt_client2_; | |
| 610 scoped_ptr<TestWindowTreeClientImpl> wt_client3_; | |
| 611 | |
| 612 mojom::WindowTreeHostPtr host_; | |
| 613 | |
| 614 private: | |
| 615 scoped_ptr<WindowTreeClientFactory> client_factory_; | |
| 616 int connection_id_1_; | |
| 617 int connection_id_2_; | |
| 618 Id root_window_id_; | |
| 619 | |
| 620 DISALLOW_COPY_AND_ASSIGN(WindowTreeAppTest); | |
| 621 }; | |
| 622 | |
| 623 // Verifies two clients/connections get different ids. | |
| 624 TEST_F(WindowTreeAppTest, TwoClientsGetDifferentConnectionIds) { | |
| 625 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 626 | |
| 627 ASSERT_EQ(1u, changes2()->size()); | |
| 628 ASSERT_NE(connection_id_1(), connection_id_2()); | |
| 629 } | |
| 630 | |
| 631 // Verifies when Embed() is invoked any child windows are removed. | |
| 632 TEST_F(WindowTreeAppTest, WindowsRemovedWhenEmbedding) { | |
| 633 // Two windows 1 and 2. 2 is parented to 1. | |
| 634 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 635 ASSERT_TRUE(window_1_1); | |
| 636 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 637 | |
| 638 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 639 ASSERT_TRUE(window_1_2); | |
| 640 ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2)); | |
| 641 | |
| 642 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 643 ASSERT_EQ(1u, changes2()->size()); | |
| 644 ASSERT_EQ(1u, (*changes2())[0].windows.size()); | |
| 645 EXPECT_EQ("[" + WindowParentToString(window_1_1, kNullParentId) + "]", | |
| 646 ChangeWindowDescription(*changes2())); | |
| 647 | |
| 648 // Embed() removed window 2. | |
| 649 { | |
| 650 std::vector<TestWindow> windows; | |
| 651 GetWindowTree(wt1(), window_1_2, &windows); | |
| 652 EXPECT_EQ(WindowParentToString(window_1_2, kNullParentId), | |
| 653 SingleWindowDescription(windows)); | |
| 654 } | |
| 655 | |
| 656 // ws2 should not see window 2. | |
| 657 { | |
| 658 std::vector<TestWindow> windows; | |
| 659 GetWindowTree(wt2(), window_1_1, &windows); | |
| 660 EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId), | |
| 661 SingleWindowDescription(windows)); | |
| 662 } | |
| 663 { | |
| 664 std::vector<TestWindow> windows; | |
| 665 GetWindowTree(wt2(), window_1_2, &windows); | |
| 666 EXPECT_TRUE(windows.empty()); | |
| 667 } | |
| 668 | |
| 669 // Windows 3 and 4 in connection 2. | |
| 670 Id window_2_3 = wt_client2()->NewWindow(3); | |
| 671 Id window_2_4 = wt_client2()->NewWindow(4); | |
| 672 ASSERT_TRUE(window_2_3); | |
| 673 ASSERT_TRUE(window_2_4); | |
| 674 ASSERT_TRUE(wt_client2()->AddWindow(window_2_3, window_2_4)); | |
| 675 | |
| 676 // Connection 3 rooted at 2. | |
| 677 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(wt2(), window_2_3)); | |
| 678 | |
| 679 // Window 4 should no longer have a parent. | |
| 680 { | |
| 681 std::vector<TestWindow> windows; | |
| 682 GetWindowTree(wt2(), window_2_3, &windows); | |
| 683 EXPECT_EQ(WindowParentToString(window_2_3, kNullParentId), | |
| 684 SingleWindowDescription(windows)); | |
| 685 | |
| 686 windows.clear(); | |
| 687 GetWindowTree(wt2(), window_2_4, &windows); | |
| 688 EXPECT_EQ(WindowParentToString(window_2_4, kNullParentId), | |
| 689 SingleWindowDescription(windows)); | |
| 690 } | |
| 691 | |
| 692 // And window 4 should not be visible to connection 3. | |
| 693 { | |
| 694 std::vector<TestWindow> windows; | |
| 695 GetWindowTree(wt3(), window_2_3, &windows); | |
| 696 EXPECT_EQ("no windows", SingleWindowDescription(windows)); | |
| 697 } | |
| 698 } | |
| 699 | |
| 700 // Verifies once Embed() has been invoked the parent connection can't see any | |
| 701 // children. | |
| 702 TEST_F(WindowTreeAppTest, CantAccessChildrenOfEmbeddedWindow) { | |
| 703 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 704 | |
| 705 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 706 Id window_2_2 = wt_client2()->NewWindow(2); | |
| 707 ASSERT_TRUE(window_2_2); | |
| 708 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_2)); | |
| 709 | |
| 710 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(wt2(), window_2_2)); | |
| 711 | |
| 712 Id window_3_3 = wt_client3()->NewWindow(3); | |
| 713 ASSERT_TRUE(window_3_3); | |
| 714 ASSERT_TRUE( | |
| 715 wt_client3()->AddWindow(wt_client3()->root_window_id(), window_3_3)); | |
| 716 | |
| 717 // Even though 3 is a child of 2 connection 2 can't see 3 as it's from a | |
| 718 // different connection. | |
| 719 { | |
| 720 std::vector<TestWindow> windows; | |
| 721 GetWindowTree(wt2(), window_2_2, &windows); | |
| 722 EXPECT_EQ(WindowParentToString(window_2_2, window_1_1), | |
| 723 SingleWindowDescription(windows)); | |
| 724 } | |
| 725 | |
| 726 // Connection 2 shouldn't be able to get window 3 at all. | |
| 727 { | |
| 728 std::vector<TestWindow> windows; | |
| 729 GetWindowTree(wt2(), window_3_3, &windows); | |
| 730 EXPECT_TRUE(windows.empty()); | |
| 731 } | |
| 732 | |
| 733 // Connection 1 should be able to see it all (its the root). | |
| 734 { | |
| 735 std::vector<TestWindow> windows; | |
| 736 GetWindowTree(wt1(), window_1_1, &windows); | |
| 737 ASSERT_EQ(3u, windows.size()); | |
| 738 EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId), | |
| 739 windows[0].ToString()); | |
| 740 // NOTE: we expect a match of WindowParentToString(window_2_2, window_1_1), | |
| 741 // but the ids are in the id space of client2, which is not the same as | |
| 742 // the id space of wt1(). | |
| 743 EXPECT_EQ("window=2,1 parent=1,1", windows[1].ToString()); | |
| 744 // Same thing here, we really want to test for | |
| 745 // WindowParentToString(window_3_3, window_2_2). | |
| 746 EXPECT_EQ("window=3,1 parent=2,1", windows[2].ToString()); | |
| 747 } | |
| 748 } | |
| 749 | |
| 750 // Verifies once Embed() has been invoked the parent can't mutate the children. | |
| 751 TEST_F(WindowTreeAppTest, CantModifyChildrenOfEmbeddedWindow) { | |
| 752 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 753 | |
| 754 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 755 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 756 ASSERT_TRUE(window_2_1); | |
| 757 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 758 | |
| 759 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(wt2(), window_2_1)); | |
| 760 | |
| 761 Id window_2_2 = wt_client2()->NewWindow(2); | |
| 762 ASSERT_TRUE(window_2_2); | |
| 763 // Connection 2 shouldn't be able to add anything to the window anymore. | |
| 764 ASSERT_FALSE(wt_client2()->AddWindow(window_2_1, window_2_2)); | |
| 765 | |
| 766 // Create window 3 in connection 3 and add it to window 3. | |
| 767 Id window_3_1 = wt_client3()->NewWindow(1); | |
| 768 ASSERT_TRUE(window_3_1); | |
| 769 ASSERT_TRUE(wt_client3()->AddWindow(window_2_1, window_3_1)); | |
| 770 | |
| 771 // Connection 2 shouldn't be able to remove window 3. | |
| 772 ASSERT_FALSE(wt_client2()->RemoveWindowFromParent(window_3_1)); | |
| 773 } | |
| 774 | |
| 775 // Verifies client gets a valid id. | |
| 776 TEST_F(WindowTreeAppTest, NewWindow) { | |
| 777 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 778 ASSERT_TRUE(window_1_1); | |
| 779 EXPECT_TRUE(changes1()->empty()); | |
| 780 | |
| 781 // Can't create a window with the same id. | |
| 782 ASSERT_EQ(0u, wt_client1()->NewWindowWithCompleteId(window_1_1)); | |
| 783 EXPECT_TRUE(changes1()->empty()); | |
| 784 | |
| 785 // Can't create a window with a bogus connection id. | |
| 786 ASSERT_EQ(0u, wt_client1()->NewWindowWithCompleteId( | |
| 787 BuildWindowId(connection_id_1() + 1, 1))); | |
| 788 EXPECT_TRUE(changes1()->empty()); | |
| 789 } | |
| 790 | |
| 791 // Verifies AddWindow fails when window is already in position. | |
| 792 TEST_F(WindowTreeAppTest, AddWindowWithNoChange) { | |
| 793 // Create the embed point now so that the ids line up. | |
| 794 ASSERT_TRUE(wt_client1()->NewWindow(1)); | |
| 795 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 796 Id window_1_3 = wt_client1()->NewWindow(3); | |
| 797 ASSERT_TRUE(window_1_2); | |
| 798 ASSERT_TRUE(window_1_3); | |
| 799 | |
| 800 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 801 | |
| 802 // Make 3 a child of 2. | |
| 803 ASSERT_TRUE(wt_client1()->AddWindow(window_1_2, window_1_3)); | |
| 804 | |
| 805 // Try again, this should fail. | |
| 806 EXPECT_FALSE(wt_client1()->AddWindow(window_1_2, window_1_3)); | |
| 807 } | |
| 808 | |
| 809 // Verifies AddWindow fails when window is already in position. | |
| 810 TEST_F(WindowTreeAppTest, AddAncestorFails) { | |
| 811 // Create the embed point now so that the ids line up. | |
| 812 ASSERT_TRUE(wt_client1()->NewWindow(1)); | |
| 813 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 814 Id window_1_3 = wt_client1()->NewWindow(3); | |
| 815 ASSERT_TRUE(window_1_2); | |
| 816 ASSERT_TRUE(window_1_3); | |
| 817 | |
| 818 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 819 | |
| 820 // Make 3 a child of 2. | |
| 821 ASSERT_TRUE(wt_client1()->AddWindow(window_1_2, window_1_3)); | |
| 822 | |
| 823 // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3. | |
| 824 EXPECT_FALSE(wt_client1()->AddWindow(window_1_3, window_1_2)); | |
| 825 } | |
| 826 | |
| 827 // Verifies adding to root sends right notifications. | |
| 828 TEST_F(WindowTreeAppTest, AddToRoot) { | |
| 829 // Create the embed point now so that the ids line up. | |
| 830 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 831 ASSERT_TRUE(window_1_1); | |
| 832 Id window_1_21 = wt_client1()->NewWindow(21); | |
| 833 Id window_1_3 = wt_client1()->NewWindow(3); | |
| 834 ASSERT_TRUE(window_1_21); | |
| 835 ASSERT_TRUE(window_1_3); | |
| 836 | |
| 837 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 838 changes2()->clear(); | |
| 839 | |
| 840 // Make 3 a child of 21. | |
| 841 ASSERT_TRUE(wt_client1()->AddWindow(window_1_21, window_1_3)); | |
| 842 | |
| 843 // Make 21 a child of 1. | |
| 844 ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_21)); | |
| 845 | |
| 846 // Connection 2 should not be told anything (because the window is from a | |
| 847 // different connection). Create a window to ensure we got a response from | |
| 848 // the server. | |
| 849 ASSERT_TRUE(wt_client2()->NewWindow(100)); | |
| 850 EXPECT_TRUE(changes2()->empty()); | |
| 851 } | |
| 852 | |
| 853 // Verifies HierarchyChanged is correctly sent for various adds/removes. | |
| 854 TEST_F(WindowTreeAppTest, WindowHierarchyChangedWindows) { | |
| 855 // Create the embed point now so that the ids line up. | |
| 856 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 857 // 1,2->1,11. | |
| 858 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 859 ASSERT_TRUE(window_1_2); | |
| 860 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true)); | |
| 861 Id window_1_11 = wt_client1()->NewWindow(11); | |
| 862 ASSERT_TRUE(window_1_11); | |
| 863 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_11, true)); | |
| 864 ASSERT_TRUE(wt_client1()->AddWindow(window_1_2, window_1_11)); | |
| 865 | |
| 866 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 867 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true)); | |
| 868 | |
| 869 ASSERT_TRUE(wt_client2()->WaitForAllMessages()); | |
| 870 changes2()->clear(); | |
| 871 | |
| 872 // 1,1->1,2->1,11 | |
| 873 { | |
| 874 // Client 2 should not get anything (1,2 is from another connection). | |
| 875 ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2)); | |
| 876 ASSERT_TRUE(wt_client2()->WaitForAllMessages()); | |
| 877 EXPECT_TRUE(changes2()->empty()); | |
| 878 } | |
| 879 | |
| 880 // 0,1->1,1->1,2->1,11. | |
| 881 { | |
| 882 // Client 2 is now connected to the root, so it should have gotten a drawn | |
| 883 // notification. | |
| 884 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 885 wt_client2_->WaitForChangeCount(1u); | |
| 886 EXPECT_EQ( | |
| 887 "DrawnStateChanged window=" + IdToString(window_1_1) + " drawn=true", | |
| 888 SingleChangeToDescription(*changes2())); | |
| 889 } | |
| 890 | |
| 891 // 1,1->1,2->1,11. | |
| 892 { | |
| 893 // Client 2 is no longer connected to the root, should get drawn state | |
| 894 // changed. | |
| 895 changes2()->clear(); | |
| 896 ASSERT_TRUE(wt_client1()->RemoveWindowFromParent(window_1_1)); | |
| 897 wt_client2_->WaitForChangeCount(1); | |
| 898 EXPECT_EQ( | |
| 899 "DrawnStateChanged window=" + IdToString(window_1_1) + " drawn=false", | |
| 900 SingleChangeToDescription(*changes2())); | |
| 901 } | |
| 902 | |
| 903 // 1,1->1,2->1,11->1,111. | |
| 904 Id window_1_111 = wt_client1()->NewWindow(111); | |
| 905 ASSERT_TRUE(window_1_111); | |
| 906 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_111, true)); | |
| 907 { | |
| 908 changes2()->clear(); | |
| 909 ASSERT_TRUE(wt_client1()->AddWindow(window_1_11, window_1_111)); | |
| 910 ASSERT_TRUE(wt_client2()->WaitForAllMessages()); | |
| 911 EXPECT_TRUE(changes2()->empty()); | |
| 912 } | |
| 913 | |
| 914 // 0,1->1,1->1,2->1,11->1,111 | |
| 915 { | |
| 916 changes2()->clear(); | |
| 917 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 918 wt_client2_->WaitForChangeCount(1); | |
| 919 EXPECT_EQ( | |
| 920 "DrawnStateChanged window=" + IdToString(window_1_1) + " drawn=true", | |
| 921 SingleChangeToDescription(*changes2())); | |
| 922 } | |
| 923 } | |
| 924 | |
| 925 TEST_F(WindowTreeAppTest, WindowHierarchyChangedAddingKnownToUnknown) { | |
| 926 // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no | |
| 927 // parent). | |
| 928 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 929 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 930 | |
| 931 Id window_2_11 = wt_client2()->NewWindow(11); | |
| 932 Id window_2_2 = wt_client2()->NewWindow(2); | |
| 933 Id window_2_21 = wt_client2()->NewWindow(21); | |
| 934 ASSERT_TRUE(window_2_11); | |
| 935 ASSERT_TRUE(window_2_2); | |
| 936 ASSERT_TRUE(window_2_21); | |
| 937 | |
| 938 // Set up the hierarchy. | |
| 939 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 940 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_11)); | |
| 941 ASSERT_TRUE(wt_client2()->AddWindow(window_2_2, window_2_21)); | |
| 942 | |
| 943 // Remove 11, should result in a hierarchy change for the root. | |
| 944 { | |
| 945 changes1()->clear(); | |
| 946 ASSERT_TRUE(wt_client2()->RemoveWindowFromParent(window_2_11)); | |
| 947 | |
| 948 wt_client1_->WaitForChangeCount(1); | |
| 949 // 2,1 should be IdToString(window_2_11), but window_2_11 is in the id | |
| 950 // space of client2, not client1. | |
| 951 EXPECT_EQ("HierarchyChanged window=2,1 new_parent=null old_parent=" + | |
| 952 IdToString(window_1_1), | |
| 953 SingleChangeToDescription(*changes1())); | |
| 954 } | |
| 955 | |
| 956 // Add 2 to 1. | |
| 957 { | |
| 958 changes1()->clear(); | |
| 959 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_2)); | |
| 960 wt_client1_->WaitForChangeCount(1); | |
| 961 EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_2) + | |
| 962 " new_parent=" + IdToString(window_1_1) + " old_parent=null", | |
| 963 SingleChangeToDescription(*changes1())); | |
| 964 // "window=2,3 parent=2,2]" should be, | |
| 965 // WindowParentToString(window_2_21, window_2_2), but isn't because of | |
| 966 // differing id spaces. | |
| 967 EXPECT_EQ("[" + WindowParentToString(window_2_2, window_1_1) + | |
| 968 "],[window=2,3 parent=2,2]", | |
| 969 ChangeWindowDescription(*changes1())); | |
| 970 } | |
| 971 } | |
| 972 | |
| 973 TEST_F(WindowTreeAppTest, ReorderWindow) { | |
| 974 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 975 | |
| 976 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 977 Id window_2_2 = wt_client2()->NewWindow(2); | |
| 978 Id window_2_3 = wt_client2()->NewWindow(3); | |
| 979 Id window_1_4 = wt_client1()->NewWindow(4); // Peer to 1,1 | |
| 980 Id window_1_5 = wt_client1()->NewWindow(5); // Peer to 1,1 | |
| 981 Id window_2_6 = wt_client2()->NewWindow(6); // Child of 1,2. | |
| 982 Id window_2_7 = wt_client2()->NewWindow(7); // Unparented. | |
| 983 Id window_2_8 = wt_client2()->NewWindow(8); // Unparented. | |
| 984 ASSERT_TRUE(window_2_1); | |
| 985 ASSERT_TRUE(window_2_2); | |
| 986 ASSERT_TRUE(window_2_3); | |
| 987 ASSERT_TRUE(window_1_4); | |
| 988 ASSERT_TRUE(window_1_5); | |
| 989 ASSERT_TRUE(window_2_6); | |
| 990 ASSERT_TRUE(window_2_7); | |
| 991 ASSERT_TRUE(window_2_8); | |
| 992 | |
| 993 ASSERT_TRUE(wt_client2()->AddWindow(window_2_1, window_2_2)); | |
| 994 ASSERT_TRUE(wt_client2()->AddWindow(window_2_2, window_2_6)); | |
| 995 ASSERT_TRUE(wt_client2()->AddWindow(window_2_1, window_2_3)); | |
| 996 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_4)); | |
| 997 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_5)); | |
| 998 ASSERT_TRUE( | |
| 999 wt_client2()->AddWindow(BuildWindowId(connection_id_1(), 1), window_2_1)); | |
| 1000 | |
| 1001 { | |
| 1002 changes1()->clear(); | |
| 1003 ASSERT_TRUE(wt_client2()->ReorderWindow(window_2_2, window_2_3, | |
| 1004 mojom::OrderDirection::ABOVE)); | |
| 1005 | |
| 1006 wt_client1_->WaitForChangeCount(1); | |
| 1007 EXPECT_EQ("Reordered window=" + IdToString(window_2_2) + " relative=" + | |
| 1008 IdToString(window_2_3) + " direction=above", | |
| 1009 SingleChangeToDescription(*changes1())); | |
| 1010 } | |
| 1011 | |
| 1012 { | |
| 1013 changes1()->clear(); | |
| 1014 ASSERT_TRUE(wt_client2()->ReorderWindow(window_2_2, window_2_3, | |
| 1015 mojom::OrderDirection::BELOW)); | |
| 1016 | |
| 1017 wt_client1_->WaitForChangeCount(1); | |
| 1018 EXPECT_EQ("Reordered window=" + IdToString(window_2_2) + " relative=" + | |
| 1019 IdToString(window_2_3) + " direction=below", | |
| 1020 SingleChangeToDescription(*changes1())); | |
| 1021 } | |
| 1022 | |
| 1023 // view2 is already below view3. | |
| 1024 EXPECT_FALSE(wt_client2()->ReorderWindow(window_2_2, window_2_3, | |
| 1025 mojom::OrderDirection::BELOW)); | |
| 1026 | |
| 1027 // view4 & 5 are unknown to connection2_. | |
| 1028 EXPECT_FALSE(wt_client2()->ReorderWindow(window_1_4, window_1_5, | |
| 1029 mojom::OrderDirection::ABOVE)); | |
| 1030 | |
| 1031 // view6 & view3 have different parents. | |
| 1032 EXPECT_FALSE(wt_client1()->ReorderWindow(window_2_3, window_2_6, | |
| 1033 mojom::OrderDirection::ABOVE)); | |
| 1034 | |
| 1035 // Non-existent window-ids | |
| 1036 EXPECT_FALSE(wt_client1()->ReorderWindow(BuildWindowId(connection_id_1(), 27), | |
| 1037 BuildWindowId(connection_id_1(), 28), | |
| 1038 mojom::OrderDirection::ABOVE)); | |
| 1039 | |
| 1040 // view7 & view8 are un-parented. | |
| 1041 EXPECT_FALSE(wt_client1()->ReorderWindow(window_2_7, window_2_8, | |
| 1042 mojom::OrderDirection::ABOVE)); | |
| 1043 } | |
| 1044 | |
| 1045 // Verifies DeleteWindow works. | |
| 1046 TEST_F(WindowTreeAppTest, DeleteWindow) { | |
| 1047 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1048 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1049 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 1050 ASSERT_TRUE(window_2_1); | |
| 1051 | |
| 1052 // Make 2 a child of 1. | |
| 1053 { | |
| 1054 changes1()->clear(); | |
| 1055 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 1056 wt_client1_->WaitForChangeCount(1); | |
| 1057 EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_1) + | |
| 1058 " new_parent=" + IdToString(window_1_1) + " old_parent=null", | |
| 1059 SingleChangeToDescription(*changes1())); | |
| 1060 } | |
| 1061 | |
| 1062 // Delete 2. | |
| 1063 { | |
| 1064 changes1()->clear(); | |
| 1065 changes2()->clear(); | |
| 1066 ASSERT_TRUE(wt_client2()->DeleteWindow(window_2_1)); | |
| 1067 EXPECT_TRUE(changes2()->empty()); | |
| 1068 | |
| 1069 wt_client1_->WaitForChangeCount(1); | |
| 1070 EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_1), | |
| 1071 SingleChangeToDescription(*changes1())); | |
| 1072 } | |
| 1073 } | |
| 1074 | |
| 1075 // Verifies DeleteWindow isn't allowed from a separate connection. | |
| 1076 TEST_F(WindowTreeAppTest, DeleteWindowFromAnotherConnectionDisallowed) { | |
| 1077 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1078 EXPECT_FALSE(wt_client2()->DeleteWindow(BuildWindowId(connection_id_1(), 1))); | |
| 1079 } | |
| 1080 | |
| 1081 // Verifies if a window was deleted and then reused that other clients are | |
| 1082 // properly notified. | |
| 1083 TEST_F(WindowTreeAppTest, ReuseDeletedWindowId) { | |
| 1084 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1085 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1086 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 1087 ASSERT_TRUE(window_2_1); | |
| 1088 | |
| 1089 // Add 2 to 1. | |
| 1090 { | |
| 1091 changes1()->clear(); | |
| 1092 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 1093 wt_client1_->WaitForChangeCount(1); | |
| 1094 EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_1) + | |
| 1095 " new_parent=" + IdToString(window_1_1) + " old_parent=null", | |
| 1096 SingleChangeToDescription(*changes1())); | |
| 1097 EXPECT_EQ("[" + WindowParentToString(window_2_1, window_1_1) + "]", | |
| 1098 ChangeWindowDescription(*changes1())); | |
| 1099 } | |
| 1100 | |
| 1101 // Delete 2. | |
| 1102 { | |
| 1103 changes1()->clear(); | |
| 1104 ASSERT_TRUE(wt_client2()->DeleteWindow(window_2_1)); | |
| 1105 | |
| 1106 wt_client1_->WaitForChangeCount(1); | |
| 1107 EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_1), | |
| 1108 SingleChangeToDescription(*changes1())); | |
| 1109 } | |
| 1110 | |
| 1111 // Create 2 again, and add it back to 1. Should get the same notification. | |
| 1112 window_2_1 = wt_client2()->NewWindow(2); | |
| 1113 ASSERT_TRUE(window_2_1); | |
| 1114 { | |
| 1115 changes1()->clear(); | |
| 1116 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 1117 | |
| 1118 wt_client1_->WaitForChangeCount(1); | |
| 1119 EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_1) + | |
| 1120 " new_parent=" + IdToString(window_1_1) + " old_parent=null", | |
| 1121 SingleChangeToDescription(*changes1())); | |
| 1122 EXPECT_EQ("[" + WindowParentToString(window_2_1, window_1_1) + "]", | |
| 1123 ChangeWindowDescription(*changes1())); | |
| 1124 } | |
| 1125 } | |
| 1126 | |
| 1127 // Assertions for GetWindowTree. | |
| 1128 TEST_F(WindowTreeAppTest, GetWindowTree) { | |
| 1129 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1130 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1131 | |
| 1132 // Create 11 in first connection and make it a child of 1. | |
| 1133 Id window_1_11 = wt_client1()->NewWindow(11); | |
| 1134 ASSERT_TRUE(window_1_11); | |
| 1135 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1136 ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_11)); | |
| 1137 | |
| 1138 // Create two windows in second connection, 2 and 3, both children of 1. | |
| 1139 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 1140 Id window_2_2 = wt_client2()->NewWindow(2); | |
| 1141 ASSERT_TRUE(window_2_1); | |
| 1142 ASSERT_TRUE(window_2_2); | |
| 1143 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 1144 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_2)); | |
| 1145 | |
| 1146 // Verifies GetWindowTree() on the root. The root connection sees all. | |
| 1147 { | |
| 1148 std::vector<TestWindow> windows; | |
| 1149 GetWindowTree(wt1(), root_window_id(), &windows); | |
| 1150 ASSERT_EQ(5u, windows.size()); | |
| 1151 EXPECT_EQ(WindowParentToString(root_window_id(), kNullParentId), | |
| 1152 windows[0].ToString()); | |
| 1153 EXPECT_EQ(WindowParentToString(window_1_1, root_window_id()), | |
| 1154 windows[1].ToString()); | |
| 1155 EXPECT_EQ(WindowParentToString(window_1_11, window_1_1), | |
| 1156 windows[2].ToString()); | |
| 1157 EXPECT_EQ(WindowParentToString(window_2_1, window_1_1), | |
| 1158 windows[3].ToString()); | |
| 1159 EXPECT_EQ(WindowParentToString(window_2_2, window_1_1), | |
| 1160 windows[4].ToString()); | |
| 1161 } | |
| 1162 | |
| 1163 // Verifies GetWindowTree() on the window 1,1 from wt2(). wt2() sees 1,1 as | |
| 1164 // 1,1 | |
| 1165 // is wt2()'s root and wt2() sees all the windows it created. | |
| 1166 { | |
| 1167 std::vector<TestWindow> windows; | |
| 1168 GetWindowTree(wt2(), window_1_1, &windows); | |
| 1169 ASSERT_EQ(3u, windows.size()); | |
| 1170 EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId), | |
| 1171 windows[0].ToString()); | |
| 1172 EXPECT_EQ(WindowParentToString(window_2_1, window_1_1), | |
| 1173 windows[1].ToString()); | |
| 1174 EXPECT_EQ(WindowParentToString(window_2_2, window_1_1), | |
| 1175 windows[2].ToString()); | |
| 1176 } | |
| 1177 | |
| 1178 // Connection 2 shouldn't be able to get the root tree. | |
| 1179 { | |
| 1180 std::vector<TestWindow> windows; | |
| 1181 GetWindowTree(wt2(), root_window_id(), &windows); | |
| 1182 ASSERT_EQ(0u, windows.size()); | |
| 1183 } | |
| 1184 } | |
| 1185 | |
| 1186 TEST_F(WindowTreeAppTest, SetWindowBounds) { | |
| 1187 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 1188 ASSERT_TRUE(window_1_1); | |
| 1189 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1190 | |
| 1191 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 1192 | |
| 1193 changes2()->clear(); | |
| 1194 | |
| 1195 wt_client2_->set_track_root_bounds_changes(true); | |
| 1196 | |
| 1197 wt1()->SetWindowBounds(10, window_1_1, | |
| 1198 mojo::Rect::From(gfx::Rect(0, 0, 100, 100))); | |
| 1199 ASSERT_TRUE(wt_client1()->WaitForChangeCompleted(10)); | |
| 1200 | |
| 1201 wt_client2_->WaitForChangeCount(1); | |
| 1202 EXPECT_EQ("BoundsChanged window=" + IdToString(window_1_1) + | |
| 1203 " old_bounds=0,0 0x0 new_bounds=0,0 100x100", | |
| 1204 SingleChangeToDescription(*changes2())); | |
| 1205 | |
| 1206 // Should not be possible to change the bounds of a window created by another | |
| 1207 // connection. | |
| 1208 wt2()->SetWindowBounds(11, window_1_1, | |
| 1209 mojo::Rect::From(gfx::Rect(0, 0, 0, 0))); | |
| 1210 ASSERT_FALSE(wt_client2()->WaitForChangeCompleted(11)); | |
| 1211 } | |
| 1212 | |
| 1213 // Verify AddWindow fails when trying to manipulate windows in other roots. | |
| 1214 TEST_F(WindowTreeAppTest, CantMoveWindowsFromOtherRoot) { | |
| 1215 // Create 1 and 2 in the first connection. | |
| 1216 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 1217 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 1218 ASSERT_TRUE(window_1_1); | |
| 1219 ASSERT_TRUE(window_1_2); | |
| 1220 | |
| 1221 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 1222 | |
| 1223 // Try to move 2 to be a child of 1 from connection 2. This should fail as 2 | |
| 1224 // should not be able to access 1. | |
| 1225 ASSERT_FALSE(wt_client2()->AddWindow(window_1_1, window_1_2)); | |
| 1226 | |
| 1227 // Try to reparent 1 to the root. A connection is not allowed to reparent its | |
| 1228 // roots. | |
| 1229 ASSERT_FALSE(wt_client2()->AddWindow(root_window_id(), window_1_1)); | |
| 1230 } | |
| 1231 | |
| 1232 // Verify RemoveWindowFromParent fails for windows that are descendants of the | |
| 1233 // roots. | |
| 1234 TEST_F(WindowTreeAppTest, CantRemoveWindowsInOtherRoots) { | |
| 1235 // Create 1 and 2 in the first connection and parent both to the root. | |
| 1236 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 1237 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 1238 ASSERT_TRUE(window_1_1); | |
| 1239 ASSERT_TRUE(window_1_2); | |
| 1240 | |
| 1241 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1242 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_2)); | |
| 1243 | |
| 1244 // Establish the second connection and give it the root 1. | |
| 1245 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 1246 | |
| 1247 // Connection 2 should not be able to remove window 2 or 1 from its parent. | |
| 1248 ASSERT_FALSE(wt_client2()->RemoveWindowFromParent(window_1_2)); | |
| 1249 ASSERT_FALSE(wt_client2()->RemoveWindowFromParent(window_1_1)); | |
| 1250 | |
| 1251 // Create windows 10 and 11 in 2. | |
| 1252 Id window_2_10 = wt_client2()->NewWindow(10); | |
| 1253 Id window_2_11 = wt_client2()->NewWindow(11); | |
| 1254 ASSERT_TRUE(window_2_10); | |
| 1255 ASSERT_TRUE(window_2_11); | |
| 1256 | |
| 1257 // Parent 11 to 10. | |
| 1258 ASSERT_TRUE(wt_client2()->AddWindow(window_2_10, window_2_11)); | |
| 1259 // Remove 11 from 10. | |
| 1260 ASSERT_TRUE(wt_client2()->RemoveWindowFromParent(window_2_11)); | |
| 1261 | |
| 1262 // Verify nothing was actually removed. | |
| 1263 { | |
| 1264 std::vector<TestWindow> windows; | |
| 1265 GetWindowTree(wt1(), root_window_id(), &windows); | |
| 1266 ASSERT_EQ(3u, windows.size()); | |
| 1267 EXPECT_EQ(WindowParentToString(root_window_id(), kNullParentId), | |
| 1268 windows[0].ToString()); | |
| 1269 EXPECT_EQ(WindowParentToString(window_1_1, root_window_id()), | |
| 1270 windows[1].ToString()); | |
| 1271 EXPECT_EQ(WindowParentToString(window_1_2, root_window_id()), | |
| 1272 windows[2].ToString()); | |
| 1273 } | |
| 1274 } | |
| 1275 | |
| 1276 // Verify GetWindowTree fails for windows that are not descendants of the roots. | |
| 1277 TEST_F(WindowTreeAppTest, CantGetWindowTreeOfOtherRoots) { | |
| 1278 // Create 1 and 2 in the first connection and parent both to the root. | |
| 1279 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 1280 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 1281 ASSERT_TRUE(window_1_1); | |
| 1282 ASSERT_TRUE(window_1_2); | |
| 1283 | |
| 1284 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1285 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_2)); | |
| 1286 | |
| 1287 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 1288 | |
| 1289 std::vector<TestWindow> windows; | |
| 1290 | |
| 1291 // Should get nothing for the root. | |
| 1292 GetWindowTree(wt2(), root_window_id(), &windows); | |
| 1293 ASSERT_TRUE(windows.empty()); | |
| 1294 | |
| 1295 // Should get nothing for window 2. | |
| 1296 GetWindowTree(wt2(), window_1_2, &windows); | |
| 1297 ASSERT_TRUE(windows.empty()); | |
| 1298 | |
| 1299 // Should get window 1 if asked for. | |
| 1300 GetWindowTree(wt2(), window_1_1, &windows); | |
| 1301 ASSERT_EQ(1u, windows.size()); | |
| 1302 EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId), | |
| 1303 windows[0].ToString()); | |
| 1304 } | |
| 1305 | |
| 1306 TEST_F(WindowTreeAppTest, EmbedWithSameWindowId) { | |
| 1307 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1308 changes2()->clear(); | |
| 1309 | |
| 1310 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1311 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(wt1(), window_1_1)); | |
| 1312 | |
| 1313 // Connection2 should have been told of the unembed and delete. | |
| 1314 { | |
| 1315 wt_client2_->WaitForChangeCount(2); | |
| 1316 EXPECT_EQ("OnUnembed window=" + IdToString(window_1_1), | |
| 1317 ChangesToDescription1(*changes2())[0]); | |
| 1318 EXPECT_EQ("WindowDeleted window=" + IdToString(window_1_1), | |
| 1319 ChangesToDescription1(*changes2())[1]); | |
| 1320 } | |
| 1321 | |
| 1322 // Connection2 has no root. Verify it can't see window 1,1 anymore. | |
| 1323 { | |
| 1324 std::vector<TestWindow> windows; | |
| 1325 GetWindowTree(wt2(), window_1_1, &windows); | |
| 1326 EXPECT_TRUE(windows.empty()); | |
| 1327 } | |
| 1328 } | |
| 1329 | |
| 1330 TEST_F(WindowTreeAppTest, EmbedWithSameWindowId2) { | |
| 1331 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1332 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1333 changes2()->clear(); | |
| 1334 | |
| 1335 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(wt1(), window_1_1)); | |
| 1336 | |
| 1337 // Connection2 should have been told about the unembed and delete. | |
| 1338 wt_client2_->WaitForChangeCount(2); | |
| 1339 changes2()->clear(); | |
| 1340 | |
| 1341 // Create a window in the third connection and parent it to the root. | |
| 1342 Id window_3_1 = wt_client3()->NewWindow(1); | |
| 1343 ASSERT_TRUE(window_3_1); | |
| 1344 ASSERT_TRUE(wt_client3()->AddWindow(window_1_1, window_3_1)); | |
| 1345 | |
| 1346 // Connection 1 should have been told about the add (it owns the window). | |
| 1347 { | |
| 1348 wt_client1_->WaitForChangeCount(1); | |
| 1349 EXPECT_EQ("HierarchyChanged window=" + IdToString(window_3_1) + | |
| 1350 " new_parent=" + IdToString(window_1_1) + " old_parent=null", | |
| 1351 SingleChangeToDescription(*changes1())); | |
| 1352 } | |
| 1353 | |
| 1354 // Embed 1,1 again. | |
| 1355 { | |
| 1356 changes3()->clear(); | |
| 1357 | |
| 1358 // We should get a new connection for the new embedding. | |
| 1359 scoped_ptr<TestWindowTreeClientImpl> connection4( | |
| 1360 EstablishConnectionViaEmbed(wt1(), window_1_1, nullptr)); | |
| 1361 ASSERT_TRUE(connection4.get()); | |
| 1362 EXPECT_EQ("[" + WindowParentToString(window_1_1, kNullParentId) + "]", | |
| 1363 ChangeWindowDescription(*connection4->tracker()->changes())); | |
| 1364 | |
| 1365 // And 3 should get an unembed and delete. | |
| 1366 wt_client3_->WaitForChangeCount(2); | |
| 1367 EXPECT_EQ("OnUnembed window=" + IdToString(window_1_1), | |
| 1368 ChangesToDescription1(*changes3())[0]); | |
| 1369 EXPECT_EQ("WindowDeleted window=" + IdToString(window_1_1), | |
| 1370 ChangesToDescription1(*changes3())[1]); | |
| 1371 } | |
| 1372 | |
| 1373 // wt3() has no root. Verify it can't see window 1,1 anymore. | |
| 1374 { | |
| 1375 std::vector<TestWindow> windows; | |
| 1376 GetWindowTree(wt3(), window_1_1, &windows); | |
| 1377 EXPECT_TRUE(windows.empty()); | |
| 1378 } | |
| 1379 | |
| 1380 // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as | |
| 1381 // wt3() can no longer see 1,1. | |
| 1382 { | |
| 1383 std::vector<TestWindow> windows; | |
| 1384 GetWindowTree(wt1(), window_1_1, &windows); | |
| 1385 ASSERT_EQ(1u, windows.size()); | |
| 1386 EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId), | |
| 1387 windows[0].ToString()); | |
| 1388 } | |
| 1389 | |
| 1390 // Verify wt3() can still see the window it created 3,1. | |
| 1391 { | |
| 1392 std::vector<TestWindow> windows; | |
| 1393 GetWindowTree(wt3(), window_3_1, &windows); | |
| 1394 ASSERT_EQ(1u, windows.size()); | |
| 1395 EXPECT_EQ(WindowParentToString(window_3_1, kNullParentId), | |
| 1396 windows[0].ToString()); | |
| 1397 } | |
| 1398 } | |
| 1399 | |
| 1400 // Assertions for SetWindowVisibility. | |
| 1401 TEST_F(WindowTreeAppTest, SetWindowVisibility) { | |
| 1402 // Create 1 and 2 in the first connection and parent both to the root. | |
| 1403 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 1404 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 1405 ASSERT_TRUE(window_1_1); | |
| 1406 ASSERT_TRUE(window_1_2); | |
| 1407 | |
| 1408 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1409 { | |
| 1410 std::vector<TestWindow> windows; | |
| 1411 GetWindowTree(wt1(), root_window_id(), &windows); | |
| 1412 ASSERT_EQ(2u, windows.size()); | |
| 1413 EXPECT_EQ(WindowParentToString(root_window_id(), kNullParentId) + | |
| 1414 " visible=true drawn=true", | |
| 1415 windows[0].ToString2()); | |
| 1416 EXPECT_EQ(WindowParentToString(window_1_1, root_window_id()) + | |
| 1417 " visible=false drawn=false", | |
| 1418 windows[1].ToString2()); | |
| 1419 } | |
| 1420 | |
| 1421 // Show all the windows. | |
| 1422 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true)); | |
| 1423 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true)); | |
| 1424 { | |
| 1425 std::vector<TestWindow> windows; | |
| 1426 GetWindowTree(wt1(), root_window_id(), &windows); | |
| 1427 ASSERT_EQ(2u, windows.size()); | |
| 1428 EXPECT_EQ(WindowParentToString(root_window_id(), kNullParentId) + | |
| 1429 " visible=true drawn=true", | |
| 1430 windows[0].ToString2()); | |
| 1431 EXPECT_EQ(WindowParentToString(window_1_1, root_window_id()) + | |
| 1432 " visible=true drawn=true", | |
| 1433 windows[1].ToString2()); | |
| 1434 } | |
| 1435 | |
| 1436 // Hide 1. | |
| 1437 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, false)); | |
| 1438 { | |
| 1439 std::vector<TestWindow> windows; | |
| 1440 GetWindowTree(wt1(), window_1_1, &windows); | |
| 1441 ASSERT_EQ(1u, windows.size()); | |
| 1442 EXPECT_EQ(WindowParentToString(window_1_1, root_window_id()) + | |
| 1443 " visible=false drawn=false", | |
| 1444 windows[0].ToString2()); | |
| 1445 } | |
| 1446 | |
| 1447 // Attach 2 to 1. | |
| 1448 ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2)); | |
| 1449 { | |
| 1450 std::vector<TestWindow> windows; | |
| 1451 GetWindowTree(wt1(), window_1_1, &windows); | |
| 1452 ASSERT_EQ(2u, windows.size()); | |
| 1453 EXPECT_EQ(WindowParentToString(window_1_1, root_window_id()) + | |
| 1454 " visible=false drawn=false", | |
| 1455 windows[0].ToString2()); | |
| 1456 EXPECT_EQ(WindowParentToString(window_1_2, window_1_1) + | |
| 1457 " visible=true drawn=false", | |
| 1458 windows[1].ToString2()); | |
| 1459 } | |
| 1460 | |
| 1461 // Show 1. | |
| 1462 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true)); | |
| 1463 { | |
| 1464 std::vector<TestWindow> windows; | |
| 1465 GetWindowTree(wt1(), window_1_1, &windows); | |
| 1466 ASSERT_EQ(2u, windows.size()); | |
| 1467 EXPECT_EQ(WindowParentToString(window_1_1, root_window_id()) + | |
| 1468 " visible=true drawn=true", | |
| 1469 windows[0].ToString2()); | |
| 1470 EXPECT_EQ(WindowParentToString(window_1_2, window_1_1) + | |
| 1471 " visible=true drawn=true", | |
| 1472 windows[1].ToString2()); | |
| 1473 } | |
| 1474 } | |
| 1475 | |
| 1476 // Test that we hear the cursor change in other connections. | |
| 1477 TEST_F(WindowTreeAppTest, SetCursor) { | |
| 1478 // Get a second connection to listen in. | |
| 1479 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1480 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1481 changes2()->clear(); | |
| 1482 | |
| 1483 ASSERT_TRUE( | |
| 1484 wt_client1()->SetPredefinedCursor(window_1_1, mojom::Cursor::IBEAM)); | |
| 1485 wt_client2_->WaitForChangeCount(1u); | |
| 1486 | |
| 1487 EXPECT_EQ("CursorChanged id=" + IdToString(window_1_1) + " cursor_id=4", | |
| 1488 SingleChangeToDescription(*changes2())); | |
| 1489 } | |
| 1490 | |
| 1491 // Assertions for SetWindowVisibility sending notifications. | |
| 1492 TEST_F(WindowTreeAppTest, SetWindowVisibilityNotifications) { | |
| 1493 // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root. | |
| 1494 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 1495 ASSERT_TRUE(window_1_1); | |
| 1496 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true)); | |
| 1497 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 1498 ASSERT_TRUE(window_1_2); | |
| 1499 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true)); | |
| 1500 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1501 ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2)); | |
| 1502 | |
| 1503 // Establish the second connection at 1,2. | |
| 1504 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoot(window_1_2)); | |
| 1505 | |
| 1506 // Add 2,3 as a child of 1,2. | |
| 1507 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 1508 ASSERT_TRUE(window_2_1); | |
| 1509 ASSERT_TRUE(wt_client2()->SetWindowVisibility(window_2_1, true)); | |
| 1510 ASSERT_TRUE(wt_client2()->AddWindow(window_1_2, window_2_1)); | |
| 1511 ASSERT_TRUE(wt_client1()->WaitForAllMessages()); | |
| 1512 | |
| 1513 changes2()->clear(); | |
| 1514 // Hide 1,2 from connection 1. Connection 2 should see this. | |
| 1515 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, false)); | |
| 1516 { | |
| 1517 wt_client2_->WaitForChangeCount(1); | |
| 1518 EXPECT_EQ( | |
| 1519 "VisibilityChanged window=" + IdToString(window_1_2) + " visible=false", | |
| 1520 SingleChangeToDescription(*changes2())); | |
| 1521 } | |
| 1522 | |
| 1523 changes1()->clear(); | |
| 1524 // Show 1,2 from connection 2, connection 1 should be notified. | |
| 1525 ASSERT_TRUE(wt_client2()->SetWindowVisibility(window_1_2, true)); | |
| 1526 { | |
| 1527 wt_client1_->WaitForChangeCount(1); | |
| 1528 EXPECT_EQ( | |
| 1529 "VisibilityChanged window=" + IdToString(window_1_2) + " visible=true", | |
| 1530 SingleChangeToDescription(*changes1())); | |
| 1531 } | |
| 1532 | |
| 1533 changes2()->clear(); | |
| 1534 // Hide 1,1, connection 2 should be told the draw state changed. | |
| 1535 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, false)); | |
| 1536 { | |
| 1537 wt_client2_->WaitForChangeCount(1); | |
| 1538 EXPECT_EQ( | |
| 1539 "DrawnStateChanged window=" + IdToString(window_1_2) + " drawn=false", | |
| 1540 SingleChangeToDescription(*changes2())); | |
| 1541 } | |
| 1542 | |
| 1543 changes2()->clear(); | |
| 1544 // Show 1,1 from connection 1. Connection 2 should see this. | |
| 1545 ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true)); | |
| 1546 { | |
| 1547 wt_client2_->WaitForChangeCount(1); | |
| 1548 EXPECT_EQ( | |
| 1549 "DrawnStateChanged window=" + IdToString(window_1_2) + " drawn=true", | |
| 1550 SingleChangeToDescription(*changes2())); | |
| 1551 } | |
| 1552 | |
| 1553 // Change visibility of 2,3, connection 1 should see this. | |
| 1554 changes1()->clear(); | |
| 1555 ASSERT_TRUE(wt_client2()->SetWindowVisibility(window_2_1, false)); | |
| 1556 { | |
| 1557 wt_client1_->WaitForChangeCount(1); | |
| 1558 EXPECT_EQ( | |
| 1559 "VisibilityChanged window=" + IdToString(window_2_1) + " visible=false", | |
| 1560 SingleChangeToDescription(*changes1())); | |
| 1561 } | |
| 1562 | |
| 1563 changes2()->clear(); | |
| 1564 // Remove 1,1 from the root, connection 2 should see drawn state changed. | |
| 1565 ASSERT_TRUE(wt_client1()->RemoveWindowFromParent(window_1_1)); | |
| 1566 { | |
| 1567 wt_client2_->WaitForChangeCount(1); | |
| 1568 EXPECT_EQ( | |
| 1569 "DrawnStateChanged window=" + IdToString(window_1_2) + " drawn=false", | |
| 1570 SingleChangeToDescription(*changes2())); | |
| 1571 } | |
| 1572 | |
| 1573 changes2()->clear(); | |
| 1574 // Add 1,1 back to the root, connection 2 should see drawn state changed. | |
| 1575 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1576 { | |
| 1577 wt_client2_->WaitForChangeCount(1); | |
| 1578 EXPECT_EQ( | |
| 1579 "DrawnStateChanged window=" + IdToString(window_1_2) + " drawn=true", | |
| 1580 SingleChangeToDescription(*changes2())); | |
| 1581 } | |
| 1582 } | |
| 1583 | |
| 1584 TEST_F(WindowTreeAppTest, SetWindowProperty) { | |
| 1585 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 1586 ASSERT_TRUE(window_1_1); | |
| 1587 | |
| 1588 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); | |
| 1589 changes2()->clear(); | |
| 1590 | |
| 1591 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1592 { | |
| 1593 std::vector<TestWindow> windows; | |
| 1594 GetWindowTree(wt1(), root_window_id(), &windows); | |
| 1595 ASSERT_EQ(2u, windows.size()); | |
| 1596 EXPECT_EQ(root_window_id(), windows[0].window_id); | |
| 1597 EXPECT_EQ(window_1_1, windows[1].window_id); | |
| 1598 ASSERT_EQ(0u, windows[1].properties.size()); | |
| 1599 } | |
| 1600 | |
| 1601 // Set properties on 1. | |
| 1602 changes2()->clear(); | |
| 1603 std::vector<uint8_t> one(1, '1'); | |
| 1604 ASSERT_TRUE(wt_client1()->SetWindowProperty(window_1_1, "one", &one)); | |
| 1605 { | |
| 1606 wt_client2_->WaitForChangeCount(1); | |
| 1607 EXPECT_EQ( | |
| 1608 "PropertyChanged window=" + IdToString(window_1_1) + " key=one value=1", | |
| 1609 SingleChangeToDescription(*changes2())); | |
| 1610 } | |
| 1611 | |
| 1612 // Test that our properties exist in the window tree | |
| 1613 { | |
| 1614 std::vector<TestWindow> windows; | |
| 1615 GetWindowTree(wt1(), window_1_1, &windows); | |
| 1616 ASSERT_EQ(1u, windows.size()); | |
| 1617 ASSERT_EQ(1u, windows[0].properties.size()); | |
| 1618 EXPECT_EQ(one, windows[0].properties["one"]); | |
| 1619 } | |
| 1620 | |
| 1621 changes2()->clear(); | |
| 1622 // Set back to null. | |
| 1623 ASSERT_TRUE(wt_client1()->SetWindowProperty(window_1_1, "one", NULL)); | |
| 1624 { | |
| 1625 wt_client2_->WaitForChangeCount(1); | |
| 1626 EXPECT_EQ("PropertyChanged window=" + IdToString(window_1_1) + | |
| 1627 " key=one value=NULL", | |
| 1628 SingleChangeToDescription(*changes2())); | |
| 1629 } | |
| 1630 } | |
| 1631 | |
| 1632 TEST_F(WindowTreeAppTest, OnEmbeddedAppDisconnected) { | |
| 1633 // Create connection 2 and 3. | |
| 1634 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1635 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1636 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 1637 ASSERT_TRUE(window_2_1); | |
| 1638 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 1639 changes2()->clear(); | |
| 1640 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(wt2(), window_2_1)); | |
| 1641 | |
| 1642 // Connection 1 should get a hierarchy change for window_2_1. | |
| 1643 wt_client1_->WaitForChangeCount(1); | |
| 1644 changes1()->clear(); | |
| 1645 | |
| 1646 // Close connection 3. Connection 2 (which had previously embedded 3) should | |
| 1647 // be notified of this. | |
| 1648 wt_client3_.reset(); | |
| 1649 wt_client2_->WaitForChangeCount(1); | |
| 1650 EXPECT_EQ("OnEmbeddedAppDisconnected window=" + IdToString(window_2_1), | |
| 1651 SingleChangeToDescription(*changes2())); | |
| 1652 | |
| 1653 wt_client1_->WaitForChangeCount(1); | |
| 1654 EXPECT_EQ("OnEmbeddedAppDisconnected window=" + IdToString(window_2_1), | |
| 1655 SingleChangeToDescription(*changes1())); | |
| 1656 } | |
| 1657 | |
| 1658 // Verifies when the parent of an Embed() is destroyed the embedded app gets | |
| 1659 // a WindowDeleted (and doesn't trigger a DCHECK). | |
| 1660 TEST_F(WindowTreeAppTest, OnParentOfEmbedDisconnects) { | |
| 1661 // Create connection 2 and 3. | |
| 1662 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1663 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1664 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1665 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 1666 Id window_2_2 = wt_client2()->NewWindow(2); | |
| 1667 ASSERT_TRUE(window_2_1); | |
| 1668 ASSERT_TRUE(window_2_2); | |
| 1669 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 1670 ASSERT_TRUE(wt_client2()->AddWindow(window_2_1, window_2_2)); | |
| 1671 changes2()->clear(); | |
| 1672 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(wt2(), window_2_2)); | |
| 1673 changes3()->clear(); | |
| 1674 | |
| 1675 // Close connection 2. Connection 3 should get a delete (for its root). | |
| 1676 wt_client2_.reset(); | |
| 1677 wt_client3_->WaitForChangeCount(1); | |
| 1678 EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_2), | |
| 1679 SingleChangeToDescription(*changes3())); | |
| 1680 } | |
| 1681 | |
| 1682 // Verifies WindowTreeImpl doesn't incorrectly erase from its internal | |
| 1683 // map when a window from another connection with the same window_id is removed. | |
| 1684 TEST_F(WindowTreeAppTest, DontCleanMapOnDestroy) { | |
| 1685 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1686 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1687 ASSERT_TRUE(wt_client2()->NewWindow(1)); | |
| 1688 changes1()->clear(); | |
| 1689 wt_client2_.reset(); | |
| 1690 wt_client1_->WaitForChangeCount(1); | |
| 1691 EXPECT_EQ("OnEmbeddedAppDisconnected window=" + IdToString(window_1_1), | |
| 1692 SingleChangeToDescription(*changes1())); | |
| 1693 std::vector<TestWindow> windows; | |
| 1694 GetWindowTree(wt1(), window_1_1, &windows); | |
| 1695 EXPECT_FALSE(windows.empty()); | |
| 1696 } | |
| 1697 | |
| 1698 // Verifies Embed() works when supplying a WindowTreeClient. | |
| 1699 TEST_F(WindowTreeAppTest, EmbedSupplyingWindowTreeClient) { | |
| 1700 ASSERT_TRUE(wt_client1()->NewWindow(1)); | |
| 1701 | |
| 1702 TestWindowTreeClientImpl client2; | |
| 1703 mojom::WindowTreeClientPtr client2_ptr; | |
| 1704 mojo::Binding<WindowTreeClient> client2_binding(&client2, &client2_ptr); | |
| 1705 ASSERT_TRUE(Embed(wt1(), BuildWindowId(connection_id_1(), 1), | |
| 1706 std::move(client2_ptr))); | |
| 1707 client2.WaitForOnEmbed(); | |
| 1708 EXPECT_EQ("OnEmbed", | |
| 1709 SingleChangeToDescription(*client2.tracker()->changes())); | |
| 1710 } | |
| 1711 | |
| 1712 TEST_F(WindowTreeAppTest, EmbedFailsFromOtherConnection) { | |
| 1713 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1714 | |
| 1715 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1716 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 1717 ASSERT_TRUE(window_2_1); | |
| 1718 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 1719 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(wt2(), window_2_1)); | |
| 1720 | |
| 1721 Id window_3_3 = wt_client3()->NewWindow(3); | |
| 1722 ASSERT_TRUE(window_3_3); | |
| 1723 ASSERT_TRUE(wt_client3()->AddWindow(window_2_1, window_3_3)); | |
| 1724 | |
| 1725 // 2 should not be able to embed in window_3_3 as window_3_3 was not created | |
| 1726 // by | |
| 1727 // 2. | |
| 1728 EXPECT_FALSE(EmbedUrl(connector(), wt2(), test_name(), window_3_3)); | |
| 1729 } | |
| 1730 | |
| 1731 // Verifies Embed() from window manager on another connections window works. | |
| 1732 TEST_F(WindowTreeAppTest, EmbedFromOtherConnection) { | |
| 1733 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1734 | |
| 1735 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1736 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 1737 ASSERT_TRUE(window_2_1); | |
| 1738 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 1739 | |
| 1740 changes2()->clear(); | |
| 1741 | |
| 1742 // Establish a third connection in window_2_1. | |
| 1743 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(wt1(), window_2_1)); | |
| 1744 | |
| 1745 ASSERT_TRUE(wt_client2()->WaitForAllMessages()); | |
| 1746 EXPECT_EQ(std::string(), SingleChangeToDescription(*changes2())); | |
| 1747 } | |
| 1748 | |
| 1749 TEST_F(WindowTreeAppTest, CantEmbedFromConnectionRoot) { | |
| 1750 // Shouldn't be able to embed into the root. | |
| 1751 ASSERT_FALSE(EmbedUrl(connector(), wt1(), test_name(), root_window_id())); | |
| 1752 | |
| 1753 // Even though the call above failed a WindowTreeClient was obtained. We need | |
| 1754 // to | |
| 1755 // wait for it else we throw off the next connect. | |
| 1756 WaitForWindowTreeClient(); | |
| 1757 | |
| 1758 // Don't allow a connection to embed into its own root. | |
| 1759 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1760 EXPECT_FALSE(EmbedUrl(connector(), wt2(), test_name(), | |
| 1761 BuildWindowId(connection_id_1(), 1))); | |
| 1762 | |
| 1763 // Need to wait for a WindowTreeClient for same reason as above. | |
| 1764 WaitForWindowTreeClient(); | |
| 1765 | |
| 1766 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 1767 ASSERT_TRUE(window_1_2); | |
| 1768 ASSERT_TRUE( | |
| 1769 wt_client1()->AddWindow(BuildWindowId(connection_id_1(), 1), window_1_2)); | |
| 1770 ASSERT_TRUE(wt_client3_.get() == nullptr); | |
| 1771 wt_client3_ = EstablishConnectionViaEmbedWithPolicyBitmask( | |
| 1772 wt1(), window_1_2, mojom::WindowTree::kAccessPolicyEmbedRoot, nullptr); | |
| 1773 ASSERT_TRUE(wt_client3_.get() != nullptr); | |
| 1774 | |
| 1775 // window_1_2 is ws3's root, so even though v3 is an embed root it should not | |
| 1776 // be able to Embed into itself. | |
| 1777 ASSERT_FALSE(EmbedUrl(connector(), wt3(), test_name(), window_1_2)); | |
| 1778 } | |
| 1779 | |
| 1780 // Verifies that a transient window tracks its parent's lifetime. | |
| 1781 TEST_F(WindowTreeAppTest, TransientWindowTracksTransientParentLifetime) { | |
| 1782 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); | |
| 1783 Id window_1_1 = BuildWindowId(connection_id_1(), 1); | |
| 1784 | |
| 1785 Id window_2_1 = wt_client2()->NewWindow(1); | |
| 1786 Id window_2_2 = wt_client2()->NewWindow(2); | |
| 1787 Id window_2_3 = wt_client2()->NewWindow(3); | |
| 1788 ASSERT_TRUE(window_2_1); | |
| 1789 | |
| 1790 // root -> window_1_1 -> window_2_1 | |
| 1791 // root -> window_1_1 -> window_2_2 | |
| 1792 // root -> window_1_1 -> window_2_3 | |
| 1793 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1794 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1)); | |
| 1795 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_2)); | |
| 1796 ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_3)); | |
| 1797 | |
| 1798 // window_2_2 and window_2_3 now track the lifetime of window_2_1. | |
| 1799 changes1()->clear(); | |
| 1800 wt2()->AddTransientWindow(10, window_2_1, window_2_2); | |
| 1801 wt2()->AddTransientWindow(11, window_2_1, window_2_3); | |
| 1802 wt_client1()->WaitForChangeCount(2); | |
| 1803 EXPECT_EQ("AddTransientWindow parent = " + IdToString(window_2_1) + | |
| 1804 " child = " + IdToString(window_2_2), | |
| 1805 ChangesToDescription1(*changes1())[0]); | |
| 1806 EXPECT_EQ("AddTransientWindow parent = " + IdToString(window_2_1) + | |
| 1807 " child = " + IdToString(window_2_3), | |
| 1808 ChangesToDescription1(*changes1())[1]); | |
| 1809 | |
| 1810 changes1()->clear(); | |
| 1811 wt2()->RemoveTransientWindowFromParent(12, window_2_3); | |
| 1812 wt_client1()->WaitForChangeCount(1); | |
| 1813 EXPECT_EQ("RemoveTransientWindowFromParent parent = " + | |
| 1814 IdToString(window_2_1) + " child = " + IdToString(window_2_3), | |
| 1815 SingleChangeToDescription(*changes1())); | |
| 1816 | |
| 1817 changes1()->clear(); | |
| 1818 ASSERT_TRUE(wt_client2()->DeleteWindow(window_2_1)); | |
| 1819 wt_client1()->WaitForChangeCount(2); | |
| 1820 EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_2), | |
| 1821 ChangesToDescription1(*changes1())[0]); | |
| 1822 EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_1), | |
| 1823 ChangesToDescription1(*changes1())[1]); | |
| 1824 } | |
| 1825 | |
| 1826 TEST_F(WindowTreeAppTest, Ids) { | |
| 1827 const Id window_1_100 = wt_client1()->NewWindow(100); | |
| 1828 ASSERT_TRUE(window_1_100); | |
| 1829 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_100)); | |
| 1830 | |
| 1831 // Establish the second connection at 1,100. | |
| 1832 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoot(window_1_100)); | |
| 1833 | |
| 1834 // 1,100 is the id in the wt_client1's id space. The new client should see | |
| 1835 // 2,1 (the server id). | |
| 1836 const Id window_1_100_in_ws2 = BuildWindowId(connection_id_1(), 1); | |
| 1837 EXPECT_EQ(window_1_100_in_ws2, wt_client2()->root_window_id()); | |
| 1838 | |
| 1839 // The first window created in the second connection gets a server id of 2,1 | |
| 1840 // regardless of the id the client uses. | |
| 1841 const Id window_2_101 = wt_client2()->NewWindow(101); | |
| 1842 ASSERT_TRUE(wt_client2()->AddWindow(window_1_100_in_ws2, window_2_101)); | |
| 1843 const Id window_2_101_in_ws1 = BuildWindowId(connection_id_2(), 1); | |
| 1844 wt_client1()->WaitForChangeCount(1); | |
| 1845 EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_101_in_ws1) + | |
| 1846 " new_parent=" + IdToString(window_1_100) + " old_parent=null", | |
| 1847 SingleChangeToDescription(*changes1())); | |
| 1848 changes1()->clear(); | |
| 1849 | |
| 1850 // Change the bounds of window_2_101 and make sure server gets it. | |
| 1851 wt2()->SetWindowBounds(11, window_2_101, | |
| 1852 mojo::Rect::From(gfx::Rect(1, 2, 3, 4))); | |
| 1853 ASSERT_TRUE(wt_client2()->WaitForChangeCompleted(11)); | |
| 1854 wt_client1()->WaitForChangeCount(1); | |
| 1855 EXPECT_EQ("BoundsChanged window=" + IdToString(window_2_101_in_ws1) + | |
| 1856 " old_bounds=0,0 0x0 new_bounds=1,2 3x4", | |
| 1857 SingleChangeToDescription(*changes1())); | |
| 1858 changes2()->clear(); | |
| 1859 | |
| 1860 // Remove 2_101 from wm, client1 should see the change. | |
| 1861 wt1()->RemoveWindowFromParent(12, window_2_101_in_ws1); | |
| 1862 ASSERT_TRUE(wt_client1()->WaitForChangeCompleted(12)); | |
| 1863 wt_client2()->WaitForChangeCount(1); | |
| 1864 EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_101) + | |
| 1865 " new_parent=null old_parent=" + | |
| 1866 IdToString(window_1_100_in_ws2), | |
| 1867 SingleChangeToDescription(*changes2())); | |
| 1868 } | |
| 1869 | |
| 1870 // Tests that setting capture fails when no input event has occurred, and there | |
| 1871 // is no notification of lost capture. | |
| 1872 TEST_F(WindowTreeAppTest, ExplicitCaptureWithoutInput) { | |
| 1873 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 1874 | |
| 1875 // Add the window to the root, so that they have a Display to handle input | |
| 1876 // capture. | |
| 1877 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1878 changes1()->clear(); | |
| 1879 | |
| 1880 // Since there has been no input, capture should not succeed. No lost capture | |
| 1881 // message is expected. | |
| 1882 wt1()->SetCapture(1, window_1_1); | |
| 1883 wt_client1_->WaitForAllMessages(); | |
| 1884 EXPECT_TRUE(changes1()->empty()); | |
| 1885 | |
| 1886 // Since there is no window with capture, lost capture should not be notified. | |
| 1887 wt1()->ReleaseCapture(3, window_1_1); | |
| 1888 wt_client1_->WaitForAllMessages(); | |
| 1889 EXPECT_TRUE(changes1()->empty()); | |
| 1890 } | |
| 1891 | |
| 1892 // TODO(jonross): Enable this once apptests can send input events to the server. | |
| 1893 // Enabling capture requires that the connection be processing events. | |
| 1894 TEST_F(WindowTreeAppTest, DISABLED_ExplicitCapturePropagation) { | |
| 1895 Id window_1_1 = wt_client1()->NewWindow(1); | |
| 1896 Id window_1_2 = wt_client1()->NewWindow(2); | |
| 1897 | |
| 1898 // Add the windows to the root, so that they have a Display to handle input | |
| 1899 // capture. | |
| 1900 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1)); | |
| 1901 ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_2)); | |
| 1902 | |
| 1903 changes1()->clear(); | |
| 1904 // Window 1 takes capture then Window 2 takes capture. | |
| 1905 // Verify that window 1 has lost capture. | |
| 1906 wt1()->SetCapture(1, window_1_1); | |
| 1907 wt1()->SetCapture(2, window_1_2); | |
| 1908 wt_client1_->WaitForChangeCount(1); | |
| 1909 | |
| 1910 EXPECT_EQ("OnLostCapture window=" + IdToString(window_1_1), | |
| 1911 SingleChangeToDescription(*changes1())); | |
| 1912 | |
| 1913 changes1()->clear(); | |
| 1914 // Explicitly releasing capture should not notify of lost capture. | |
| 1915 wt1()->ReleaseCapture(3, window_1_2); | |
| 1916 wt_client1_->WaitForAllMessages(); | |
| 1917 | |
| 1918 EXPECT_TRUE(changes1()->empty()); | |
| 1919 } | |
| 1920 | |
| 1921 // TODO(sky): need to better track changes to initial connection. For example, | |
| 1922 // that SetBounsdWindows/AddWindow and the like don't result in messages to the | |
| 1923 // originating connection. | |
| 1924 | |
| 1925 // TODO(sky): make sure coverage of what was | |
| 1926 // WindowManagerTest.SecondEmbedRoot_InitService and | |
| 1927 // WindowManagerTest.MultipleEmbedRootsBeforeWTHReady gets added to window | |
| 1928 // manager | |
| 1929 // tests. | |
| 1930 | |
| 1931 } // namespace test | |
| 1932 } // namespace ws | |
| 1933 } // namespace mus | |
| OLD | NEW |