| 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 "components/mus/ws/window_tree.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 | |
| 9 #include <string> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/macros.h" | |
| 13 #include "base/memory/ptr_util.h" | |
| 14 #include "base/strings/stringprintf.h" | |
| 15 #include "components/mus/common/types.h" | |
| 16 #include "components/mus/common/util.h" | |
| 17 #include "components/mus/public/interfaces/window_tree.mojom.h" | |
| 18 #include "components/mus/surfaces/surfaces_state.h" | |
| 19 #include "components/mus/ws/default_access_policy.h" | |
| 20 #include "components/mus/ws/display_binding.h" | |
| 21 #include "components/mus/ws/ids.h" | |
| 22 #include "components/mus/ws/platform_display.h" | |
| 23 #include "components/mus/ws/platform_display_factory.h" | |
| 24 #include "components/mus/ws/platform_display_init_params.h" | |
| 25 #include "components/mus/ws/server_window.h" | |
| 26 #include "components/mus/ws/server_window_surface_manager_test_api.h" | |
| 27 #include "components/mus/ws/test_change_tracker.h" | |
| 28 #include "components/mus/ws/test_server_window_delegate.h" | |
| 29 #include "components/mus/ws/test_utils.h" | |
| 30 #include "components/mus/ws/window_manager_access_policy.h" | |
| 31 #include "components/mus/ws/window_manager_display_root.h" | |
| 32 #include "components/mus/ws/window_server.h" | |
| 33 #include "components/mus/ws/window_server_delegate.h" | |
| 34 #include "components/mus/ws/window_tree_binding.h" | |
| 35 #include "services/shell/public/interfaces/connector.mojom.h" | |
| 36 #include "testing/gtest/include/gtest/gtest.h" | |
| 37 #include "ui/events/event.h" | |
| 38 #include "ui/events/event_utils.h" | |
| 39 #include "ui/gfx/geometry/rect.h" | |
| 40 | |
| 41 namespace mus { | |
| 42 namespace ws { | |
| 43 namespace test { | |
| 44 namespace { | |
| 45 | |
| 46 std::string WindowIdToString(const WindowId& id) { | |
| 47 return base::StringPrintf("%d,%d", id.client_id, id.window_id); | |
| 48 } | |
| 49 | |
| 50 ClientWindowId BuildClientWindowId(WindowTree* tree, | |
| 51 ClientSpecificId window_id) { | |
| 52 return ClientWindowId(WindowIdToTransportId(WindowId(tree->id(), window_id))); | |
| 53 } | |
| 54 | |
| 55 // ----------------------------------------------------------------------------- | |
| 56 | |
| 57 ui::PointerEvent CreatePointerDownEvent(int x, int y) { | |
| 58 return ui::PointerEvent(ui::TouchEvent(ui::ET_TOUCH_PRESSED, gfx::Point(x, y), | |
| 59 1, ui::EventTimeForNow())); | |
| 60 } | |
| 61 | |
| 62 ui::PointerEvent CreatePointerUpEvent(int x, int y) { | |
| 63 return ui::PointerEvent(ui::TouchEvent( | |
| 64 ui::ET_TOUCH_RELEASED, gfx::Point(x, y), 1, ui::EventTimeForNow())); | |
| 65 } | |
| 66 | |
| 67 ui::PointerEvent CreateMouseMoveEvent(int x, int y) { | |
| 68 return ui::PointerEvent( | |
| 69 ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(x, y), gfx::Point(x, y), | |
| 70 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE)); | |
| 71 } | |
| 72 | |
| 73 ui::PointerEvent CreateMouseDownEvent(int x, int y) { | |
| 74 return ui::PointerEvent( | |
| 75 ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(x, y), gfx::Point(x, y), | |
| 76 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, | |
| 77 ui::EF_LEFT_MOUSE_BUTTON)); | |
| 78 } | |
| 79 | |
| 80 ui::PointerEvent CreateMouseUpEvent(int x, int y) { | |
| 81 return ui::PointerEvent( | |
| 82 ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(x, y), gfx::Point(x, y), | |
| 83 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, | |
| 84 ui::EF_LEFT_MOUSE_BUTTON)); | |
| 85 } | |
| 86 | |
| 87 ServerWindow* GetCaptureWindow(Display* display) { | |
| 88 return display->GetActiveWindowManagerDisplayRoot() | |
| 89 ->window_manager_state() | |
| 90 ->capture_window(); | |
| 91 } | |
| 92 | |
| 93 mojom::EventMatcherPtr CreateEventMatcher(ui::mojom::EventType type) { | |
| 94 mojom::EventMatcherPtr matcher = mojom::EventMatcher::New(); | |
| 95 matcher->type_matcher = mojom::EventTypeMatcher::New(); | |
| 96 matcher->type_matcher->type = type; | |
| 97 return matcher; | |
| 98 } | |
| 99 | |
| 100 } // namespace | |
| 101 | |
| 102 // ----------------------------------------------------------------------------- | |
| 103 | |
| 104 class WindowTreeTest : public testing::Test { | |
| 105 public: | |
| 106 WindowTreeTest() {} | |
| 107 ~WindowTreeTest() override {} | |
| 108 | |
| 109 mus::mojom::Cursor cursor_id() { | |
| 110 return static_cast<mus::mojom::Cursor>( | |
| 111 window_event_targeting_helper_.cursor_id()); | |
| 112 } | |
| 113 Display* display() { return window_event_targeting_helper_.display(); } | |
| 114 TestWindowTreeBinding* last_binding() { | |
| 115 return window_event_targeting_helper_.last_binding(); | |
| 116 } | |
| 117 TestWindowTreeClient* last_window_tree_client() { | |
| 118 return window_event_targeting_helper_.last_window_tree_client(); | |
| 119 } | |
| 120 TestWindowTreeClient* wm_client() { | |
| 121 return window_event_targeting_helper_.wm_client(); | |
| 122 } | |
| 123 WindowServer* window_server() { | |
| 124 return window_event_targeting_helper_.window_server(); | |
| 125 } | |
| 126 WindowTree* wm_tree() { | |
| 127 return window_event_targeting_helper_.window_server()->GetTreeWithId(1); | |
| 128 } | |
| 129 | |
| 130 void DispatchEventWithoutAck(const ui::Event& event) { | |
| 131 DisplayTestApi(display()).OnEvent(event); | |
| 132 } | |
| 133 | |
| 134 void set_window_manager_internal(WindowTree* tree, | |
| 135 mojom::WindowManager* wm_internal) { | |
| 136 WindowTreeTestApi(tree).set_window_manager_internal(wm_internal); | |
| 137 } | |
| 138 | |
| 139 void AckPreviousEvent() { | |
| 140 WindowManagerStateTestApi test_api( | |
| 141 display()->GetActiveWindowManagerDisplayRoot()->window_manager_state()); | |
| 142 while (test_api.tree_awaiting_input_ack()) { | |
| 143 test_api.tree_awaiting_input_ack()->OnWindowInputEventAck( | |
| 144 0, mojom::EventResult::HANDLED); | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 void DispatchEventAndAckImmediately(const ui::Event& event) { | |
| 149 DispatchEventWithoutAck(event); | |
| 150 AckPreviousEvent(); | |
| 151 } | |
| 152 | |
| 153 // Creates a new window from wm_tree() and embeds a new client in it. | |
| 154 void SetupEventTargeting(TestWindowTreeClient** out_client, | |
| 155 WindowTree** window_tree, | |
| 156 ServerWindow** window); | |
| 157 | |
| 158 // Creates a new tree as the specified user. This does what creation via | |
| 159 // a WindowTreeFactory does. | |
| 160 WindowTree* CreateNewTree(const UserId& user_id, | |
| 161 TestWindowTreeBinding** binding) { | |
| 162 WindowTree* tree = | |
| 163 new WindowTree(window_server(), user_id, nullptr, | |
| 164 base::WrapUnique(new DefaultAccessPolicy)); | |
| 165 *binding = new TestWindowTreeBinding(tree); | |
| 166 window_server()->AddTree(base::WrapUnique(tree), base::WrapUnique(*binding), | |
| 167 nullptr); | |
| 168 return tree; | |
| 169 } | |
| 170 | |
| 171 protected: | |
| 172 WindowEventTargetingHelper window_event_targeting_helper_; | |
| 173 | |
| 174 private: | |
| 175 DISALLOW_COPY_AND_ASSIGN(WindowTreeTest); | |
| 176 }; | |
| 177 | |
| 178 // Creates a new window in wm_tree(), adds it to the root, embeds a | |
| 179 // new client in the window and creates a child of said window. |window| is | |
| 180 // set to the child of |window_tree| that is created. | |
| 181 void WindowTreeTest::SetupEventTargeting(TestWindowTreeClient** out_client, | |
| 182 WindowTree** window_tree, | |
| 183 ServerWindow** window) { | |
| 184 ServerWindow* embed_window = window_event_targeting_helper_.CreatePrimaryTree( | |
| 185 gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 50, 50)); | |
| 186 window_event_targeting_helper_.CreateSecondaryTree( | |
| 187 embed_window, gfx::Rect(20, 20, 20, 20), out_client, window_tree, window); | |
| 188 } | |
| 189 | |
| 190 // Verifies focus correctly changes on pointer events. | |
| 191 TEST_F(WindowTreeTest, FocusOnPointer) { | |
| 192 const ClientWindowId embed_window_id = BuildClientWindowId(wm_tree(), 1); | |
| 193 EXPECT_TRUE( | |
| 194 wm_tree()->NewWindow(embed_window_id, ServerWindow::Properties())); | |
| 195 ServerWindow* embed_window = wm_tree()->GetWindowByClientId(embed_window_id); | |
| 196 ASSERT_TRUE(embed_window); | |
| 197 EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id, true)); | |
| 198 ASSERT_TRUE(FirstRoot(wm_tree())); | |
| 199 const ClientWindowId wm_root_id = FirstRootId(wm_tree()); | |
| 200 EXPECT_TRUE(wm_tree()->AddWindow(wm_root_id, embed_window_id)); | |
| 201 display()->root_window()->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
| 202 mojom::WindowTreeClientPtr client; | |
| 203 mojom::WindowTreeClientRequest client_request = GetProxy(&client); | |
| 204 wm_client()->Bind(std::move(client_request)); | |
| 205 const uint32_t embed_flags = 0; | |
| 206 wm_tree()->Embed(embed_window_id, std::move(client), embed_flags); | |
| 207 WindowTree* tree1 = window_server()->GetTreeWithRoot(embed_window); | |
| 208 ASSERT_TRUE(tree1 != nullptr); | |
| 209 ASSERT_NE(tree1, wm_tree()); | |
| 210 | |
| 211 embed_window->SetBounds(gfx::Rect(0, 0, 50, 50)); | |
| 212 | |
| 213 const ClientWindowId child1_id(BuildClientWindowId(tree1, 1)); | |
| 214 EXPECT_TRUE(tree1->NewWindow(child1_id, ServerWindow::Properties())); | |
| 215 EXPECT_TRUE(tree1->AddWindow(ClientWindowIdForWindow(tree1, embed_window), | |
| 216 child1_id)); | |
| 217 ServerWindow* child1 = tree1->GetWindowByClientId(child1_id); | |
| 218 ASSERT_TRUE(child1); | |
| 219 child1->SetVisible(true); | |
| 220 child1->SetBounds(gfx::Rect(20, 20, 20, 20)); | |
| 221 EnableHitTest(child1); | |
| 222 | |
| 223 TestWindowTreeClient* tree1_client = last_window_tree_client(); | |
| 224 tree1_client->tracker()->changes()->clear(); | |
| 225 wm_client()->tracker()->changes()->clear(); | |
| 226 | |
| 227 // Focus should not go to |child1| yet, since the parent still doesn't allow | |
| 228 // active children. | |
| 229 DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); | |
| 230 Display* display1 = tree1->GetDisplay(embed_window); | |
| 231 EXPECT_EQ(nullptr, display1->GetFocusedWindow()); | |
| 232 DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22)); | |
| 233 tree1_client->tracker()->changes()->clear(); | |
| 234 wm_client()->tracker()->changes()->clear(); | |
| 235 | |
| 236 display1->AddActivationParent(embed_window); | |
| 237 | |
| 238 // Focus should go to child1. This result in notifying both the window | |
| 239 // manager and client client being notified. | |
| 240 DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); | |
| 241 EXPECT_EQ(child1, display1->GetFocusedWindow()); | |
| 242 ASSERT_GE(wm_client()->tracker()->changes()->size(), 1u); | |
| 243 EXPECT_EQ("Focused id=2,1", | |
| 244 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
| 245 ASSERT_GE(tree1_client->tracker()->changes()->size(), 1u); | |
| 246 EXPECT_EQ("Focused id=2,1", | |
| 247 ChangesToDescription1(*tree1_client->tracker()->changes())[0]); | |
| 248 | |
| 249 DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22)); | |
| 250 wm_client()->tracker()->changes()->clear(); | |
| 251 tree1_client->tracker()->changes()->clear(); | |
| 252 | |
| 253 // Press outside of the embedded window. Note that root cannot be focused | |
| 254 // (because it cannot be activated). So the focus would not move in this case. | |
| 255 DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22)); | |
| 256 EXPECT_EQ(child1, display()->GetFocusedWindow()); | |
| 257 | |
| 258 DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22)); | |
| 259 wm_client()->tracker()->changes()->clear(); | |
| 260 tree1_client->tracker()->changes()->clear(); | |
| 261 | |
| 262 // Press in the same location. Should not get a focus change event (only input | |
| 263 // event). | |
| 264 DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22)); | |
| 265 EXPECT_EQ(child1, display()->GetFocusedWindow()); | |
| 266 ASSERT_EQ(wm_client()->tracker()->changes()->size(), 1u) | |
| 267 << SingleChangeToDescription(*wm_client()->tracker()->changes()); | |
| 268 EXPECT_EQ("InputEvent window=0,3 event_action=16", | |
| 269 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
| 270 EXPECT_TRUE(tree1_client->tracker()->changes()->empty()); | |
| 271 } | |
| 272 | |
| 273 TEST_F(WindowTreeTest, BasicInputEventTarget) { | |
| 274 TestWindowTreeClient* embed_client = nullptr; | |
| 275 WindowTree* tree = nullptr; | |
| 276 ServerWindow* window = nullptr; | |
| 277 EXPECT_NO_FATAL_FAILURE( | |
| 278 SetupEventTargeting(&embed_client, &tree, &window)); | |
| 279 | |
| 280 // Send an event to |v1|. |embed_client| should get the event, not | |
| 281 // |wm_client|, since |v1| lives inside an embedded window. | |
| 282 DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); | |
| 283 ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); | |
| 284 EXPECT_EQ("Focused id=2,1", | |
| 285 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
| 286 ASSERT_EQ(2u, embed_client->tracker()->changes()->size()); | |
| 287 EXPECT_EQ("Focused id=2,1", | |
| 288 ChangesToDescription1(*embed_client->tracker()->changes())[0]); | |
| 289 EXPECT_EQ("InputEvent window=2,1 event_action=16", | |
| 290 ChangesToDescription1(*embed_client->tracker()->changes())[1]); | |
| 291 } | |
| 292 | |
| 293 // Tests that a client can observe events outside its bounds. | |
| 294 TEST_F(WindowTreeTest, SetEventObserver) { | |
| 295 // Create an embedded client. | |
| 296 TestWindowTreeClient* client = nullptr; | |
| 297 WindowTree* tree = nullptr; | |
| 298 ServerWindow* window = nullptr; | |
| 299 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&client, &tree, &window)); | |
| 300 | |
| 301 // Create an event outside the bounds of the client. | |
| 302 ui::PointerEvent pointer_down = CreatePointerDownEvent(5, 5); | |
| 303 | |
| 304 // Events are not observed before setting an observer. | |
| 305 DispatchEventAndAckImmediately(pointer_down); | |
| 306 ASSERT_EQ(0u, client->tracker()->changes()->size()); | |
| 307 | |
| 308 // Create an observer for pointer-down events. | |
| 309 WindowTreeTestApi(tree).SetEventObserver( | |
| 310 CreateEventMatcher(ui::mojom::EventType::POINTER_DOWN), 111u); | |
| 311 | |
| 312 // Pointer-down events are sent to the client. | |
| 313 DispatchEventAndAckImmediately(pointer_down); | |
| 314 ASSERT_EQ(1u, client->tracker()->changes()->size()); | |
| 315 EXPECT_EQ("EventObserved event_action=16 event_observer_id=111", | |
| 316 ChangesToDescription1(*client->tracker()->changes())[0]); | |
| 317 client->tracker()->changes()->clear(); | |
| 318 | |
| 319 // Clearing the observer stops sending events to the client. | |
| 320 WindowTreeTestApi(tree).SetEventObserver(nullptr, 0u); | |
| 321 DispatchEventAndAckImmediately(pointer_down); | |
| 322 ASSERT_EQ(0u, client->tracker()->changes()->size()); | |
| 323 } | |
| 324 | |
| 325 // Tests that a client using an event observer does not receive events that | |
| 326 // don't match the EventMatcher spec. | |
| 327 TEST_F(WindowTreeTest, SetEventObserverNonMatching) { | |
| 328 // Create an embedded client. | |
| 329 TestWindowTreeClient* client = nullptr; | |
| 330 WindowTree* tree = nullptr; | |
| 331 ServerWindow* window = nullptr; | |
| 332 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&client, &tree, &window)); | |
| 333 | |
| 334 // Create an observer for pointer-down events. | |
| 335 WindowTreeTestApi(tree).SetEventObserver( | |
| 336 CreateEventMatcher(ui::mojom::EventType::POINTER_DOWN), 111u); | |
| 337 | |
| 338 // Pointer-up events are not sent to the client, since they don't match. | |
| 339 DispatchEventAndAckImmediately(CreatePointerUpEvent(5, 5)); | |
| 340 ASSERT_EQ(0u, client->tracker()->changes()->size()); | |
| 341 } | |
| 342 | |
| 343 // Tests that an event that both hits a client window and matches an event | |
| 344 // observer is sent only once to the client. | |
| 345 TEST_F(WindowTreeTest, SetEventObserverSendsOnce) { | |
| 346 // Create an embedded client. | |
| 347 TestWindowTreeClient* client = nullptr; | |
| 348 WindowTree* tree = nullptr; | |
| 349 ServerWindow* window = nullptr; | |
| 350 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&client, &tree, &window)); | |
| 351 | |
| 352 // Create an observer for pointer-up events (which do not cause focus | |
| 353 // changes). | |
| 354 WindowTreeTestApi(tree).SetEventObserver( | |
| 355 CreateEventMatcher(ui::mojom::EventType::POINTER_UP), 111u); | |
| 356 | |
| 357 // Create an event inside the bounds of the client. | |
| 358 ui::PointerEvent pointer_up = CreatePointerUpEvent(25, 25); | |
| 359 | |
| 360 // The event is dispatched once, with a flag set that it matched the event | |
| 361 // observer. | |
| 362 DispatchEventAndAckImmediately(pointer_up); | |
| 363 ASSERT_EQ(1u, client->tracker()->changes()->size()); | |
| 364 EXPECT_EQ("InputEvent window=2,1 event_action=18 event_observer_id=111", | |
| 365 SingleChangeToDescription(*client->tracker()->changes())); | |
| 366 } | |
| 367 | |
| 368 // Tests that events generated by user A are not observed by event observers for | |
| 369 // user B. | |
| 370 TEST_F(WindowTreeTest, SetEventObserverWrongUser) { | |
| 371 // Embed a window tree belonging to a different user. | |
| 372 TestWindowTreeBinding* other_binding; | |
| 373 WindowTree* other_tree = CreateNewTree("other_user", &other_binding); | |
| 374 other_binding->client()->tracker()->changes()->clear(); | |
| 375 | |
| 376 // Set event observers on both the wm tree and the other user's tree. | |
| 377 WindowTreeTestApi(wm_tree()).SetEventObserver( | |
| 378 CreateEventMatcher(ui::mojom::EventType::POINTER_UP), 111u); | |
| 379 WindowTreeTestApi(other_tree) | |
| 380 .SetEventObserver(CreateEventMatcher(ui::mojom::EventType::POINTER_UP), | |
| 381 222u); | |
| 382 | |
| 383 // An event is observed by the wm tree, but not by the other user's tree. | |
| 384 DispatchEventAndAckImmediately(CreatePointerUpEvent(5, 5)); | |
| 385 ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); | |
| 386 EXPECT_EQ("InputEvent window=0,3 event_action=18 event_observer_id=111", | |
| 387 SingleChangeToDescription(*wm_client()->tracker()->changes())); | |
| 388 ASSERT_EQ(0u, other_binding->client()->tracker()->changes()->size()); | |
| 389 } | |
| 390 | |
| 391 // Tests that an event observer cannot observe keystrokes. | |
| 392 TEST_F(WindowTreeTest, SetEventObserverKeyEventsDisallowed) { | |
| 393 WindowTreeTestApi(wm_tree()).SetEventObserver( | |
| 394 CreateEventMatcher(ui::mojom::EventType::KEY_PRESSED), 111u); | |
| 395 ui::KeyEvent key_pressed(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE); | |
| 396 DispatchEventAndAckImmediately(key_pressed); | |
| 397 EXPECT_EQ(0u, wm_client()->tracker()->changes()->size()); | |
| 398 | |
| 399 WindowTreeTestApi(wm_tree()).SetEventObserver( | |
| 400 CreateEventMatcher(ui::mojom::EventType::KEY_RELEASED), 222u); | |
| 401 ui::KeyEvent key_released(ui::ET_KEY_RELEASED, ui::VKEY_A, ui::EF_NONE); | |
| 402 DispatchEventAndAckImmediately(key_released); | |
| 403 EXPECT_EQ(0u, wm_client()->tracker()->changes()->size()); | |
| 404 } | |
| 405 | |
| 406 TEST_F(WindowTreeTest, CursorChangesWhenMouseOverWindowAndWindowSetsCursor) { | |
| 407 TestWindowTreeClient* embed_client = nullptr; | |
| 408 WindowTree* tree = nullptr; | |
| 409 ServerWindow* window = nullptr; | |
| 410 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
| 411 | |
| 412 // Like in BasicInputEventTarget, we send a pointer down event to be | |
| 413 // dispatched. This is only to place the mouse cursor over that window though. | |
| 414 DispatchEventAndAckImmediately(CreateMouseMoveEvent(21, 22)); | |
| 415 | |
| 416 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
| 417 | |
| 418 // Because the cursor is over the window when SetCursor was called, we should | |
| 419 // have immediately changed the cursor. | |
| 420 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
| 421 } | |
| 422 | |
| 423 TEST_F(WindowTreeTest, CursorChangesWhenEnteringWindowWithDifferentCursor) { | |
| 424 TestWindowTreeClient* embed_client = nullptr; | |
| 425 WindowTree* tree = nullptr; | |
| 426 ServerWindow* window = nullptr; | |
| 427 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
| 428 | |
| 429 // Let's create a pointer event outside the window and then move the pointer | |
| 430 // inside. | |
| 431 DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5)); | |
| 432 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
| 433 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
| 434 | |
| 435 DispatchEventAndAckImmediately(CreateMouseMoveEvent(21, 22)); | |
| 436 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
| 437 } | |
| 438 | |
| 439 TEST_F(WindowTreeTest, TouchesDontChangeCursor) { | |
| 440 TestWindowTreeClient* embed_client = nullptr; | |
| 441 WindowTree* tree = nullptr; | |
| 442 ServerWindow* window = nullptr; | |
| 443 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
| 444 | |
| 445 // Let's create a pointer event outside the window and then move the pointer | |
| 446 // inside. | |
| 447 DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5)); | |
| 448 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
| 449 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
| 450 | |
| 451 // With a touch event, we shouldn't update the cursor. | |
| 452 DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); | |
| 453 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
| 454 } | |
| 455 | |
| 456 TEST_F(WindowTreeTest, DragOutsideWindow) { | |
| 457 TestWindowTreeClient* embed_client = nullptr; | |
| 458 WindowTree* tree = nullptr; | |
| 459 ServerWindow* window = nullptr; | |
| 460 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
| 461 | |
| 462 // Start with the cursor outside the window. Setting the cursor shouldn't | |
| 463 // change the cursor. | |
| 464 DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5)); | |
| 465 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
| 466 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
| 467 | |
| 468 // Move the pointer to the inside of the window | |
| 469 DispatchEventAndAckImmediately(CreateMouseMoveEvent(21, 22)); | |
| 470 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
| 471 | |
| 472 // Start the drag. | |
| 473 DispatchEventAndAckImmediately(CreateMouseDownEvent(21, 22)); | |
| 474 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
| 475 | |
| 476 // Move the cursor (mouse is still down) outside the window. | |
| 477 DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5)); | |
| 478 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
| 479 | |
| 480 // Release the cursor. We should now adapt the cursor of the window | |
| 481 // underneath the pointer. | |
| 482 DispatchEventAndAckImmediately(CreateMouseUpEvent(5, 5)); | |
| 483 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
| 484 } | |
| 485 | |
| 486 TEST_F(WindowTreeTest, ChangingWindowBoundsChangesCursor) { | |
| 487 TestWindowTreeClient* embed_client = nullptr; | |
| 488 WindowTree* tree = nullptr; | |
| 489 ServerWindow* window = nullptr; | |
| 490 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
| 491 | |
| 492 // Put the cursor just outside the bounds of the window. | |
| 493 DispatchEventAndAckImmediately(CreateMouseMoveEvent(41, 41)); | |
| 494 window->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
| 495 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
| 496 | |
| 497 // Expand the bounds of the window so they now include where the cursor now | |
| 498 // is. | |
| 499 window->SetBounds(gfx::Rect(20, 20, 25, 25)); | |
| 500 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
| 501 | |
| 502 // Contract the bounds again. | |
| 503 window->SetBounds(gfx::Rect(20, 20, 20, 20)); | |
| 504 EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id()); | |
| 505 } | |
| 506 | |
| 507 TEST_F(WindowTreeTest, WindowReorderingChangesCursor) { | |
| 508 TestWindowTreeClient* embed_client = nullptr; | |
| 509 WindowTree* tree = nullptr; | |
| 510 ServerWindow* window1 = nullptr; | |
| 511 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window1)); | |
| 512 | |
| 513 // Create a second window right over the first. | |
| 514 const ClientWindowId embed_window_id(FirstRootId(tree)); | |
| 515 const ClientWindowId child2_id(BuildClientWindowId(tree, 2)); | |
| 516 EXPECT_TRUE(tree->NewWindow(child2_id, ServerWindow::Properties())); | |
| 517 ServerWindow* child2 = tree->GetWindowByClientId(child2_id); | |
| 518 ASSERT_TRUE(child2); | |
| 519 EXPECT_TRUE(tree->AddWindow(embed_window_id, child2_id)); | |
| 520 child2->SetVisible(true); | |
| 521 child2->SetBounds(gfx::Rect(20, 20, 20, 20)); | |
| 522 EnableHitTest(child2); | |
| 523 | |
| 524 // Give each window a different cursor. | |
| 525 window1->SetPredefinedCursor(mojom::Cursor::IBEAM); | |
| 526 child2->SetPredefinedCursor(mojom::Cursor::HAND); | |
| 527 | |
| 528 // We expect window2 to be over window1 now. | |
| 529 DispatchEventAndAckImmediately(CreateMouseMoveEvent(22, 22)); | |
| 530 EXPECT_EQ(mojom::Cursor::HAND, cursor_id()); | |
| 531 | |
| 532 // But when we put window2 at the bottom, we should adapt window1's cursor. | |
| 533 child2->parent()->StackChildAtBottom(child2); | |
| 534 EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id()); | |
| 535 } | |
| 536 | |
| 537 TEST_F(WindowTreeTest, EventAck) { | |
| 538 const ClientWindowId embed_window_id = BuildClientWindowId(wm_tree(), 1); | |
| 539 EXPECT_TRUE( | |
| 540 wm_tree()->NewWindow(embed_window_id, ServerWindow::Properties())); | |
| 541 EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id, true)); | |
| 542 ASSERT_TRUE(FirstRoot(wm_tree())); | |
| 543 EXPECT_TRUE(wm_tree()->AddWindow(FirstRootId(wm_tree()), embed_window_id)); | |
| 544 display()->root_window()->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
| 545 | |
| 546 wm_client()->tracker()->changes()->clear(); | |
| 547 DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22)); | |
| 548 ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); | |
| 549 EXPECT_EQ("InputEvent window=0,3 event_action=17", | |
| 550 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
| 551 wm_client()->tracker()->changes()->clear(); | |
| 552 | |
| 553 // Send another event. This event shouldn't reach the client. | |
| 554 DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22)); | |
| 555 ASSERT_EQ(0u, wm_client()->tracker()->changes()->size()); | |
| 556 | |
| 557 // Ack the first event. That should trigger the dispatch of the second event. | |
| 558 AckPreviousEvent(); | |
| 559 ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); | |
| 560 EXPECT_EQ("InputEvent window=0,3 event_action=17", | |
| 561 ChangesToDescription1(*wm_client()->tracker()->changes())[0]); | |
| 562 } | |
| 563 | |
| 564 // Establish client, call NewTopLevelWindow(), make sure get id, and make | |
| 565 // sure client paused. | |
| 566 TEST_F(WindowTreeTest, NewTopLevelWindow) { | |
| 567 TestWindowManager wm_internal; | |
| 568 set_window_manager_internal(wm_tree(), &wm_internal); | |
| 569 | |
| 570 TestWindowTreeBinding* child_binding; | |
| 571 WindowTree* child_tree = CreateNewTree(wm_tree()->user_id(), &child_binding); | |
| 572 child_binding->client()->tracker()->changes()->clear(); | |
| 573 child_binding->client()->set_record_on_change_completed(true); | |
| 574 | |
| 575 // Create a new top level window. | |
| 576 mojo::Map<mojo::String, mojo::Array<uint8_t>> properties; | |
| 577 const uint32_t initial_change_id = 17; | |
| 578 // Explicitly use an id that does not contain the client id. | |
| 579 const ClientWindowId embed_window_id2_in_child(45 << 16 | 27); | |
| 580 static_cast<mojom::WindowTree*>(child_tree) | |
| 581 ->NewTopLevelWindow(initial_change_id, embed_window_id2_in_child.id, | |
| 582 std::move(properties)); | |
| 583 | |
| 584 // The binding should be paused until the wm acks the change. | |
| 585 uint32_t wm_change_id = 0u; | |
| 586 ASSERT_TRUE(wm_internal.did_call_create_top_level_window(&wm_change_id)); | |
| 587 EXPECT_TRUE(child_binding->is_paused()); | |
| 588 | |
| 589 // Create the window for |embed_window_id2_in_child|. | |
| 590 const ClientWindowId embed_window_id2 = BuildClientWindowId(wm_tree(), 2); | |
| 591 EXPECT_TRUE( | |
| 592 wm_tree()->NewWindow(embed_window_id2, ServerWindow::Properties())); | |
| 593 EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id2, true)); | |
| 594 EXPECT_TRUE(wm_tree()->AddWindow(FirstRootId(wm_tree()), embed_window_id2)); | |
| 595 | |
| 596 // Ack the change, which should resume the binding. | |
| 597 child_binding->client()->tracker()->changes()->clear(); | |
| 598 static_cast<mojom::WindowManagerClient*>(wm_tree()) | |
| 599 ->OnWmCreatedTopLevelWindow(wm_change_id, embed_window_id2.id); | |
| 600 EXPECT_FALSE(child_binding->is_paused()); | |
| 601 EXPECT_EQ("TopLevelCreated id=17 window_id=" + | |
| 602 WindowIdToString( | |
| 603 WindowIdFromTransportId(embed_window_id2_in_child.id)) + | |
| 604 " drawn=true", | |
| 605 SingleChangeToDescription( | |
| 606 *child_binding->client()->tracker()->changes())); | |
| 607 child_binding->client()->tracker()->changes()->clear(); | |
| 608 | |
| 609 // Change the visibility of the window from the owner and make sure the | |
| 610 // client sees the right id. | |
| 611 ServerWindow* embed_window = wm_tree()->GetWindowByClientId(embed_window_id2); | |
| 612 ASSERT_TRUE(embed_window); | |
| 613 EXPECT_TRUE(embed_window->visible()); | |
| 614 ASSERT_TRUE(wm_tree()->SetWindowVisibility( | |
| 615 ClientWindowIdForWindow(wm_tree(), embed_window), false)); | |
| 616 EXPECT_FALSE(embed_window->visible()); | |
| 617 EXPECT_EQ("VisibilityChanged window=" + | |
| 618 WindowIdToString( | |
| 619 WindowIdFromTransportId(embed_window_id2_in_child.id)) + | |
| 620 " visible=false", | |
| 621 SingleChangeToDescription( | |
| 622 *child_binding->client()->tracker()->changes())); | |
| 623 | |
| 624 // Set the visibility from the child using the client assigned id. | |
| 625 ASSERT_TRUE(child_tree->SetWindowVisibility(embed_window_id2_in_child, true)); | |
| 626 EXPECT_TRUE(embed_window->visible()); | |
| 627 } | |
| 628 | |
| 629 // Tests that only the capture window can release capture. | |
| 630 TEST_F(WindowTreeTest, ExplicitSetCapture) { | |
| 631 TestWindowTreeClient* embed_client = nullptr; | |
| 632 WindowTree* tree = nullptr; | |
| 633 ServerWindow* window = nullptr; | |
| 634 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
| 635 const ServerWindow* root_window = *tree->roots().begin(); | |
| 636 tree->AddWindow(FirstRootId(tree), ClientWindowIdForWindow(tree, window)); | |
| 637 window->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
| 638 ASSERT_TRUE(tree->GetDisplay(window)); | |
| 639 | |
| 640 // Set capture. | |
| 641 mojom::WindowTree* mojom_window_tree = static_cast<mojom::WindowTree*>(tree); | |
| 642 uint32_t change_id = 42; | |
| 643 mojom_window_tree->SetCapture(change_id, WindowIdToTransportId(window->id())); | |
| 644 Display* display = tree->GetDisplay(window); | |
| 645 EXPECT_EQ(window, GetCaptureWindow(display)); | |
| 646 | |
| 647 // Only the capture window should be able to release capture | |
| 648 mojom_window_tree->ReleaseCapture(++change_id, | |
| 649 WindowIdToTransportId(root_window->id())); | |
| 650 EXPECT_EQ(window, GetCaptureWindow(display)); | |
| 651 mojom_window_tree->ReleaseCapture(++change_id, | |
| 652 WindowIdToTransportId(window->id())); | |
| 653 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
| 654 } | |
| 655 | |
| 656 // Tests that while a client is interacting with input, that capture is not | |
| 657 // allowed for invisible windows. | |
| 658 TEST_F(WindowTreeTest, CaptureWindowMustBeVisible) { | |
| 659 TestWindowTreeClient* embed_client = nullptr; | |
| 660 WindowTree* tree = nullptr; | |
| 661 ServerWindow* window = nullptr; | |
| 662 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
| 663 tree->AddWindow(FirstRootId(tree), ClientWindowIdForWindow(tree, window)); | |
| 664 window->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
| 665 ASSERT_TRUE(tree->GetDisplay(window)); | |
| 666 | |
| 667 DispatchEventWithoutAck(CreatePointerDownEvent(10, 10)); | |
| 668 window->SetVisible(false); | |
| 669 EXPECT_FALSE(tree->SetCapture(ClientWindowIdForWindow(tree, window))); | |
| 670 EXPECT_NE(window, GetCaptureWindow(tree->GetDisplay(window))); | |
| 671 } | |
| 672 | |
| 673 // Tests that showing a modal window releases the capture if the capture is on a | |
| 674 // descendant of the modal parent. | |
| 675 TEST_F(WindowTreeTest, ShowModalWindowWithDescendantCapture) { | |
| 676 TestWindowTreeClient* embed_client = nullptr; | |
| 677 WindowTree* tree = nullptr; | |
| 678 ServerWindow* w1 = nullptr; | |
| 679 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
| 680 | |
| 681 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
| 682 const ServerWindow* root_window = *tree->roots().begin(); | |
| 683 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
| 684 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
| 685 Display* display = tree->GetDisplay(w1); | |
| 686 | |
| 687 // Create |w11| as a child of |w1| and make it visible. | |
| 688 ClientWindowId w11_id = BuildClientWindowId(tree, 11); | |
| 689 ASSERT_TRUE(tree->NewWindow(w11_id, ServerWindow::Properties())); | |
| 690 ServerWindow* w11 = tree->GetWindowByClientId(w11_id); | |
| 691 w11->SetBounds(gfx::Rect(10, 10, 10, 10)); | |
| 692 ASSERT_TRUE(tree->AddWindow(w1_id, w11_id)); | |
| 693 ASSERT_TRUE(tree->SetWindowVisibility(w11_id, true)); | |
| 694 | |
| 695 // Create |w2| as a child of |root_window| and modal to |w1| and leave it | |
| 696 // hidden. | |
| 697 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
| 698 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
| 699 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
| 700 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
| 701 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
| 702 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
| 703 ASSERT_TRUE(tree->SetModal(w2_id)); | |
| 704 | |
| 705 // Set capture to |w11|. | |
| 706 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
| 707 ASSERT_TRUE(tree->SetCapture(w11_id)); | |
| 708 EXPECT_EQ(w11, GetCaptureWindow(display)); | |
| 709 AckPreviousEvent(); | |
| 710 | |
| 711 // Make |w2| visible. This should release capture as capture is set to a | |
| 712 // descendant of the modal parent. | |
| 713 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
| 714 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
| 715 } | |
| 716 | |
| 717 // Tests that setting a visible window as modal releases the capture if the | |
| 718 // capture is on a descendant of the modal parent. | |
| 719 TEST_F(WindowTreeTest, VisibleWindowToModalWithDescendantCapture) { | |
| 720 TestWindowTreeClient* embed_client = nullptr; | |
| 721 WindowTree* tree = nullptr; | |
| 722 ServerWindow* w1 = nullptr; | |
| 723 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
| 724 | |
| 725 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
| 726 const ServerWindow* root_window = *tree->roots().begin(); | |
| 727 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
| 728 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
| 729 Display* display = tree->GetDisplay(w1); | |
| 730 | |
| 731 // Create |w11| as a child of |w1| and make it visible. | |
| 732 ClientWindowId w11_id = BuildClientWindowId(tree, 11); | |
| 733 ASSERT_TRUE(tree->NewWindow(w11_id, ServerWindow::Properties())); | |
| 734 ServerWindow* w11 = tree->GetWindowByClientId(w11_id); | |
| 735 w11->SetBounds(gfx::Rect(10, 10, 10, 10)); | |
| 736 ASSERT_TRUE(tree->AddWindow(w1_id, w11_id)); | |
| 737 ASSERT_TRUE(tree->SetWindowVisibility(w11_id, true)); | |
| 738 | |
| 739 // Create |w2| as a child of |root_window| and make it visible. | |
| 740 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
| 741 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
| 742 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
| 743 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
| 744 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
| 745 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
| 746 | |
| 747 // Set capture to |w11|. | |
| 748 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
| 749 ASSERT_TRUE(tree->SetCapture(w11_id)); | |
| 750 EXPECT_EQ(w11, GetCaptureWindow(display)); | |
| 751 AckPreviousEvent(); | |
| 752 | |
| 753 // Set |w2| modal to |w1|. This should release the capture as the capture is | |
| 754 // set to a descendant of the modal parent. | |
| 755 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
| 756 ASSERT_TRUE(tree->SetModal(w2_id)); | |
| 757 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
| 758 } | |
| 759 | |
| 760 // Tests that showing a modal window does not change capture if the capture is | |
| 761 // not on a descendant of the modal parent. | |
| 762 TEST_F(WindowTreeTest, ShowModalWindowWithNonDescendantCapture) { | |
| 763 TestWindowTreeClient* embed_client = nullptr; | |
| 764 WindowTree* tree = nullptr; | |
| 765 ServerWindow* w1 = nullptr; | |
| 766 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
| 767 | |
| 768 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
| 769 const ServerWindow* root_window = *tree->roots().begin(); | |
| 770 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
| 771 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
| 772 Display* display = tree->GetDisplay(w1); | |
| 773 | |
| 774 // Create |w2| as a child of |root_window| and modal to |w1| and leave it | |
| 775 // hidden.. | |
| 776 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
| 777 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
| 778 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
| 779 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
| 780 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
| 781 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
| 782 ASSERT_TRUE(tree->SetModal(w2_id)); | |
| 783 | |
| 784 // Create |w3| as a child of |root_window| and make it visible. | |
| 785 ClientWindowId w3_id = BuildClientWindowId(tree, 3); | |
| 786 ASSERT_TRUE(tree->NewWindow(w3_id, ServerWindow::Properties())); | |
| 787 ServerWindow* w3 = tree->GetWindowByClientId(w3_id); | |
| 788 w3->SetBounds(gfx::Rect(70, 10, 10, 10)); | |
| 789 ASSERT_TRUE(tree->AddWindow(root_window_id, w3_id)); | |
| 790 ASSERT_TRUE(tree->SetWindowVisibility(w3_id, true)); | |
| 791 | |
| 792 // Set capture to |w3|. | |
| 793 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
| 794 ASSERT_TRUE(tree->SetCapture(w3_id)); | |
| 795 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
| 796 AckPreviousEvent(); | |
| 797 | |
| 798 // Make |w2| visible. This should not change the capture as the capture is not | |
| 799 // set to a descendant of the modal parent. | |
| 800 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
| 801 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
| 802 } | |
| 803 | |
| 804 // Tests that setting a visible window as modal does not change the capture if | |
| 805 // the capture is not set to a descendant of the modal parent. | |
| 806 TEST_F(WindowTreeTest, VisibleWindowToModalWithNonDescendantCapture) { | |
| 807 TestWindowTreeClient* embed_client = nullptr; | |
| 808 WindowTree* tree = nullptr; | |
| 809 ServerWindow* w1 = nullptr; | |
| 810 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
| 811 | |
| 812 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
| 813 const ServerWindow* root_window = *tree->roots().begin(); | |
| 814 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
| 815 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
| 816 Display* display = tree->GetDisplay(w1); | |
| 817 | |
| 818 // Create |w2| and |w3| as children of |root_window| and make them visible. | |
| 819 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
| 820 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
| 821 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
| 822 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
| 823 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
| 824 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
| 825 | |
| 826 ClientWindowId w3_id = BuildClientWindowId(tree, 3); | |
| 827 ASSERT_TRUE(tree->NewWindow(w3_id, ServerWindow::Properties())); | |
| 828 ServerWindow* w3 = tree->GetWindowByClientId(w3_id); | |
| 829 w3->SetBounds(gfx::Rect(70, 10, 10, 10)); | |
| 830 ASSERT_TRUE(tree->AddWindow(root_window_id, w3_id)); | |
| 831 ASSERT_TRUE(tree->SetWindowVisibility(w3_id, true)); | |
| 832 | |
| 833 // Set capture to |w3|. | |
| 834 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
| 835 ASSERT_TRUE(tree->SetCapture(w3_id)); | |
| 836 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
| 837 AckPreviousEvent(); | |
| 838 | |
| 839 // Set |w2| modal to |w1|. This should not release the capture as the capture | |
| 840 // is not set to a descendant of the modal parent. | |
| 841 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
| 842 ASSERT_TRUE(tree->SetModal(w2_id)); | |
| 843 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
| 844 } | |
| 845 | |
| 846 // Tests that showing a system modal window releases the capture. | |
| 847 TEST_F(WindowTreeTest, ShowSystemModalWindowWithCapture) { | |
| 848 TestWindowTreeClient* embed_client = nullptr; | |
| 849 WindowTree* tree = nullptr; | |
| 850 ServerWindow* w1 = nullptr; | |
| 851 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
| 852 | |
| 853 w1->SetBounds(gfx::Rect(10, 10, 10, 10)); | |
| 854 const ServerWindow* root_window = *tree->roots().begin(); | |
| 855 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
| 856 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
| 857 Display* display = tree->GetDisplay(w1); | |
| 858 | |
| 859 // Create a system modal window |w2| as a child of |root_window| and leave it | |
| 860 // hidden. | |
| 861 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
| 862 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
| 863 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
| 864 w2->SetBounds(gfx::Rect(30, 10, 10, 10)); | |
| 865 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
| 866 ASSERT_TRUE(tree->SetModal(w2_id)); | |
| 867 | |
| 868 // Set capture to |w1|. | |
| 869 DispatchEventWithoutAck(CreatePointerDownEvent(15, 15)); | |
| 870 ASSERT_TRUE(tree->SetCapture(w1_id)); | |
| 871 EXPECT_EQ(w1, GetCaptureWindow(display)); | |
| 872 AckPreviousEvent(); | |
| 873 | |
| 874 // Make |w2| visible. This should release capture as it is system modal | |
| 875 // window. | |
| 876 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
| 877 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
| 878 } | |
| 879 | |
| 880 // Tests that setting a visible window as modal to system releases the capture. | |
| 881 TEST_F(WindowTreeTest, VisibleWindowToSystemModalWithCapture) { | |
| 882 TestWindowTreeClient* embed_client = nullptr; | |
| 883 WindowTree* tree = nullptr; | |
| 884 ServerWindow* w1 = nullptr; | |
| 885 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
| 886 | |
| 887 w1->SetBounds(gfx::Rect(10, 10, 10, 10)); | |
| 888 const ServerWindow* root_window = *tree->roots().begin(); | |
| 889 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
| 890 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
| 891 Display* display = tree->GetDisplay(w1); | |
| 892 | |
| 893 // Create |w2| as a child of |root_window| and make it visible. | |
| 894 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
| 895 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
| 896 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
| 897 w2->SetBounds(gfx::Rect(30, 10, 10, 10)); | |
| 898 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
| 899 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
| 900 | |
| 901 // Set capture to |w1|. | |
| 902 DispatchEventWithoutAck(CreatePointerDownEvent(15, 15)); | |
| 903 ASSERT_TRUE(tree->SetCapture(w1_id)); | |
| 904 EXPECT_EQ(w1, GetCaptureWindow(display)); | |
| 905 AckPreviousEvent(); | |
| 906 | |
| 907 // Make |w2| modal to system. This should release capture. | |
| 908 ASSERT_TRUE(tree->SetModal(w2_id)); | |
| 909 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
| 910 } | |
| 911 | |
| 912 // Tests that moving the capture window to a modal parent releases the capture | |
| 913 // as capture cannot be blocked by a modal window. | |
| 914 TEST_F(WindowTreeTest, MoveCaptureWindowToModalParent) { | |
| 915 TestWindowTreeClient* embed_client = nullptr; | |
| 916 WindowTree* tree = nullptr; | |
| 917 ServerWindow* w1 = nullptr; | |
| 918 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1)); | |
| 919 | |
| 920 w1->SetBounds(gfx::Rect(10, 10, 30, 30)); | |
| 921 const ServerWindow* root_window = *tree->roots().begin(); | |
| 922 ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window); | |
| 923 ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1); | |
| 924 Display* display = tree->GetDisplay(w1); | |
| 925 | |
| 926 // Create |w2| and |w3| as children of |root_window| and make them visible. | |
| 927 ClientWindowId w2_id = BuildClientWindowId(tree, 2); | |
| 928 ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties())); | |
| 929 ServerWindow* w2 = tree->GetWindowByClientId(w2_id); | |
| 930 w2->SetBounds(gfx::Rect(50, 10, 10, 10)); | |
| 931 ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id)); | |
| 932 ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true)); | |
| 933 | |
| 934 ClientWindowId w3_id = BuildClientWindowId(tree, 3); | |
| 935 ASSERT_TRUE(tree->NewWindow(w3_id, ServerWindow::Properties())); | |
| 936 ServerWindow* w3 = tree->GetWindowByClientId(w3_id); | |
| 937 w3->SetBounds(gfx::Rect(70, 10, 10, 10)); | |
| 938 ASSERT_TRUE(tree->AddWindow(root_window_id, w3_id)); | |
| 939 ASSERT_TRUE(tree->SetWindowVisibility(w3_id, true)); | |
| 940 | |
| 941 // Set |w2| modal to |w1|. | |
| 942 ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id)); | |
| 943 ASSERT_TRUE(tree->SetModal(w2_id)); | |
| 944 | |
| 945 // Set capture to |w3|. | |
| 946 DispatchEventWithoutAck(CreatePointerDownEvent(25, 25)); | |
| 947 ASSERT_TRUE(tree->SetCapture(w3_id)); | |
| 948 EXPECT_EQ(w3, GetCaptureWindow(display)); | |
| 949 AckPreviousEvent(); | |
| 950 | |
| 951 // Make |w3| child of |w1|. This should release capture as |w3| is now blocked | |
| 952 // by a modal window. | |
| 953 ASSERT_TRUE(tree->AddWindow(w1_id, w3_id)); | |
| 954 EXPECT_EQ(nullptr, GetCaptureWindow(display)); | |
| 955 } | |
| 956 | |
| 957 // Tests that opacity can be set on a known window. | |
| 958 TEST_F(WindowTreeTest, SetOpacity) { | |
| 959 TestWindowTreeClient* embed_client = nullptr; | |
| 960 WindowTree* tree = nullptr; | |
| 961 ServerWindow* window = nullptr; | |
| 962 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
| 963 | |
| 964 const float new_opacity = 0.5f; | |
| 965 EXPECT_NE(new_opacity, window->opacity()); | |
| 966 ASSERT_TRUE(tree->SetWindowOpacity(ClientWindowIdForWindow(tree, window), | |
| 967 new_opacity)); | |
| 968 EXPECT_EQ(new_opacity, window->opacity()); | |
| 969 | |
| 970 // Re-applying the same opacity will succeed. | |
| 971 EXPECT_TRUE(tree->SetWindowOpacity(ClientWindowIdForWindow(tree, window), | |
| 972 new_opacity)); | |
| 973 } | |
| 974 | |
| 975 // Tests that opacity requests for unknown windows are rejected. | |
| 976 TEST_F(WindowTreeTest, SetOpacityFailsOnUnknownWindow) { | |
| 977 TestWindowTreeClient* embed_client = nullptr; | |
| 978 WindowTree* tree = nullptr; | |
| 979 ServerWindow* window = nullptr; | |
| 980 EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window)); | |
| 981 | |
| 982 TestServerWindowDelegate delegate; | |
| 983 WindowId window_id(42, 1337); | |
| 984 ServerWindow unknown_window(&delegate, window_id); | |
| 985 const float new_opacity = 0.5f; | |
| 986 ASSERT_NE(new_opacity, unknown_window.opacity()); | |
| 987 | |
| 988 EXPECT_FALSE(tree->SetWindowOpacity( | |
| 989 ClientWindowId(WindowIdToTransportId(window_id)), new_opacity)); | |
| 990 EXPECT_NE(new_opacity, unknown_window.opacity()); | |
| 991 } | |
| 992 | |
| 993 TEST_F(WindowTreeTest, SetCaptureTargetsRightConnection) { | |
| 994 ServerWindow* window = window_event_targeting_helper_.CreatePrimaryTree( | |
| 995 gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 50, 50)); | |
| 996 WindowTree* owning_tree = | |
| 997 window_server()->GetTreeWithId(window->id().client_id); | |
| 998 WindowTree* embed_tree = window_server()->GetTreeWithRoot(window); | |
| 999 ASSERT_NE(owning_tree, embed_tree); | |
| 1000 ASSERT_TRUE( | |
| 1001 owning_tree->SetCapture(ClientWindowIdForWindow(owning_tree, window))); | |
| 1002 DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22)); | |
| 1003 WindowManagerStateTestApi wm_state_test_api( | |
| 1004 display()->GetActiveWindowManagerDisplayRoot()->window_manager_state()); | |
| 1005 EXPECT_EQ(owning_tree, wm_state_test_api.tree_awaiting_input_ack()); | |
| 1006 AckPreviousEvent(); | |
| 1007 | |
| 1008 // Set capture from the embedded client and make sure it gets the event. | |
| 1009 ASSERT_TRUE( | |
| 1010 embed_tree->SetCapture(ClientWindowIdForWindow(embed_tree, window))); | |
| 1011 DispatchEventWithoutAck(CreateMouseMoveEvent(22, 23)); | |
| 1012 EXPECT_EQ(embed_tree, wm_state_test_api.tree_awaiting_input_ack()); | |
| 1013 } | |
| 1014 | |
| 1015 } // namespace test | |
| 1016 } // namespace ws | |
| 1017 } // namespace mus | |
| OLD | NEW |