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