| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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 | |
| 6 #include "base/callback.h" | |
| 7 #include "base/macros.h" | |
| 8 #include "base/memory/ptr_util.h" | |
| 9 #include "services/ui/public/cpp/property_type_converters.h" | |
| 10 #include "services/ui/public/cpp/tests/window_tree_client_private.h" | |
| 11 #include "services/ui/public/cpp/window.h" | |
| 12 #include "services/ui/public/cpp/window_observer.h" | |
| 13 #include "services/ui/public/cpp/window_property.h" | |
| 14 #include "services/ui/public/cpp/window_tree_client.h" | |
| 15 #include "services/ui/public/interfaces/window_manager.mojom.h" | |
| 16 #include "services/ui/public/interfaces/window_tree.mojom.h" | |
| 17 #include "testing/gtest/include/gtest/gtest.h" | |
| 18 #include "third_party/skia/include/core/SkBitmap.h" | |
| 19 #include "third_party/skia/include/core/SkColor.h" | |
| 20 #include "ui/aura/mus/property_converter.h" | |
| 21 #include "ui/aura/window.h" | |
| 22 #include "ui/events/event.h" | |
| 23 #include "ui/events/test/test_event_handler.h" | |
| 24 #include "ui/gfx/geometry/rect.h" | |
| 25 #include "ui/gfx/image/image_skia.h" | |
| 26 #include "ui/gfx/path.h" | |
| 27 #include "ui/gfx/skia_util.h" | |
| 28 #include "ui/views/controls/native/native_view_host.h" | |
| 29 #include "ui/views/mus/native_widget_mus.h" | |
| 30 #include "ui/views/mus/window_manager_connection.h" | |
| 31 #include "ui/views/test/focus_manager_test.h" | |
| 32 #include "ui/views/test/views_test_base.h" | |
| 33 #include "ui/views/widget/widget.h" | |
| 34 #include "ui/views/widget/widget_delegate.h" | |
| 35 #include "ui/views/widget/widget_observer.h" | |
| 36 #include "ui/wm/public/activation_client.h" | |
| 37 | |
| 38 using ui::mojom::EventResult; | |
| 39 | |
| 40 namespace views { | |
| 41 namespace { | |
| 42 | |
| 43 // A view that reports any mouse press as handled. | |
| 44 class HandleMousePressView : public View { | |
| 45 public: | |
| 46 HandleMousePressView() {} | |
| 47 ~HandleMousePressView() override {} | |
| 48 | |
| 49 // View: | |
| 50 bool OnMousePressed(const ui::MouseEvent& event) override { return true; } | |
| 51 | |
| 52 private: | |
| 53 DISALLOW_COPY_AND_ASSIGN(HandleMousePressView); | |
| 54 }; | |
| 55 | |
| 56 // A view that deletes a widget on mouse press. | |
| 57 class DeleteWidgetView : public View { | |
| 58 public: | |
| 59 explicit DeleteWidgetView(std::unique_ptr<Widget>* widget_ptr) | |
| 60 : widget_ptr_(widget_ptr) {} | |
| 61 ~DeleteWidgetView() override {} | |
| 62 | |
| 63 // View: | |
| 64 bool OnMousePressed(const ui::MouseEvent& event) override { | |
| 65 widget_ptr_->reset(); | |
| 66 return true; | |
| 67 } | |
| 68 | |
| 69 private: | |
| 70 std::unique_ptr<Widget>* widget_ptr_; | |
| 71 DISALLOW_COPY_AND_ASSIGN(DeleteWidgetView); | |
| 72 }; | |
| 73 | |
| 74 // Returns a small colored bitmap. | |
| 75 SkBitmap MakeBitmap(SkColor color) { | |
| 76 SkBitmap bitmap; | |
| 77 bitmap.allocN32Pixels(8, 8); | |
| 78 bitmap.eraseColor(color); | |
| 79 return bitmap; | |
| 80 } | |
| 81 | |
| 82 // An observer that tracks widget activation changes. | |
| 83 class WidgetActivationObserver : public WidgetObserver { | |
| 84 public: | |
| 85 explicit WidgetActivationObserver(Widget* widget) : widget_(widget) { | |
| 86 widget_->AddObserver(this); | |
| 87 } | |
| 88 | |
| 89 ~WidgetActivationObserver() override { | |
| 90 widget_->RemoveObserver(this); | |
| 91 } | |
| 92 | |
| 93 const std::vector<bool>& changes() const { return changes_; } | |
| 94 | |
| 95 // WidgetObserver: | |
| 96 void OnWidgetActivationChanged(Widget* widget, bool active) override { | |
| 97 ASSERT_EQ(widget_, widget); | |
| 98 changes_.push_back(active); | |
| 99 } | |
| 100 | |
| 101 private: | |
| 102 Widget* widget_; | |
| 103 std::vector<bool> changes_; | |
| 104 | |
| 105 DISALLOW_COPY_AND_ASSIGN(WidgetActivationObserver); | |
| 106 }; | |
| 107 | |
| 108 // A WidgetDelegate that supplies app and window icons. | |
| 109 class TestWidgetDelegate : public WidgetDelegateView { | |
| 110 public: | |
| 111 explicit TestWidgetDelegate(const SkBitmap& app_icon, | |
| 112 const SkBitmap& window_icon) | |
| 113 : app_icon_(gfx::ImageSkia::CreateFrom1xBitmap(app_icon)), | |
| 114 window_icon_(gfx::ImageSkia::CreateFrom1xBitmap(window_icon)) {} | |
| 115 | |
| 116 ~TestWidgetDelegate() override {} | |
| 117 | |
| 118 void SetAppIcon(const SkBitmap& icon) { | |
| 119 app_icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon); | |
| 120 } | |
| 121 | |
| 122 void SetWindowIcon(const SkBitmap& icon) { | |
| 123 window_icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon); | |
| 124 } | |
| 125 | |
| 126 // views::WidgetDelegate: | |
| 127 gfx::ImageSkia GetWindowAppIcon() override { return app_icon_; } | |
| 128 gfx::ImageSkia GetWindowIcon() override { return window_icon_; } | |
| 129 | |
| 130 private: | |
| 131 gfx::ImageSkia app_icon_; | |
| 132 gfx::ImageSkia window_icon_; | |
| 133 | |
| 134 DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); | |
| 135 }; | |
| 136 | |
| 137 class WidgetDelegateWithHitTestMask : public WidgetDelegateView { | |
| 138 public: | |
| 139 explicit WidgetDelegateWithHitTestMask(const gfx::Rect& mask_rect) | |
| 140 : mask_rect_(mask_rect) {} | |
| 141 | |
| 142 ~WidgetDelegateWithHitTestMask() override {} | |
| 143 | |
| 144 // views::WidgetDelegate: | |
| 145 bool WidgetHasHitTestMask() const override { return true; } | |
| 146 void GetWidgetHitTestMask(gfx::Path* mask) const override { | |
| 147 mask->addRect(gfx::RectToSkRect(mask_rect_)); | |
| 148 } | |
| 149 | |
| 150 private: | |
| 151 gfx::Rect mask_rect_; | |
| 152 | |
| 153 DISALLOW_COPY_AND_ASSIGN(WidgetDelegateWithHitTestMask); | |
| 154 }; | |
| 155 | |
| 156 } // namespace | |
| 157 | |
| 158 class NativeWidgetMusTest : public ViewsTestBase { | |
| 159 public: | |
| 160 NativeWidgetMusTest() {} | |
| 161 ~NativeWidgetMusTest() override {} | |
| 162 | |
| 163 // Creates a test widget. Takes ownership of |delegate|. | |
| 164 std::unique_ptr<Widget> CreateWidget(WidgetDelegate* delegate) { | |
| 165 std::unique_ptr<Widget> widget(new Widget()); | |
| 166 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); | |
| 167 params.delegate = delegate; | |
| 168 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 169 params.bounds = initial_bounds(); | |
| 170 widget->Init(params); | |
| 171 return widget; | |
| 172 } | |
| 173 | |
| 174 int ack_callback_count() { return ack_callback_count_; } | |
| 175 | |
| 176 void AckCallback(ui::mojom::EventResult result) { | |
| 177 ack_callback_count_++; | |
| 178 EXPECT_EQ(ui::mojom::EventResult::HANDLED, result); | |
| 179 } | |
| 180 | |
| 181 // Returns a mouse pressed event inside the widget. Tests that place views | |
| 182 // within the widget that respond to the event must be constructed within the | |
| 183 // widget coordinate space such that they respond correctly. | |
| 184 std::unique_ptr<ui::MouseEvent> CreateMouseEvent() { | |
| 185 return base::MakeUnique<ui::MouseEvent>( | |
| 186 ui::ET_MOUSE_PRESSED, gfx::Point(50, 50), gfx::Point(50, 50), | |
| 187 base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); | |
| 188 } | |
| 189 | |
| 190 // Simulates an input event to the NativeWidget. | |
| 191 void OnWindowInputEvent( | |
| 192 NativeWidgetMus* native_widget, | |
| 193 const ui::Event& event, | |
| 194 std::unique_ptr<base::Callback<void(ui::mojom::EventResult)>>* | |
| 195 ack_callback) { | |
| 196 native_widget->OnWindowInputEvent(native_widget->window(), event, | |
| 197 ack_callback); | |
| 198 } | |
| 199 | |
| 200 protected: | |
| 201 gfx::Rect initial_bounds() { return gfx::Rect(10, 20, 100, 200); } | |
| 202 | |
| 203 private: | |
| 204 int ack_callback_count_ = 0; | |
| 205 | |
| 206 DISALLOW_COPY_AND_ASSIGN(NativeWidgetMusTest); | |
| 207 }; | |
| 208 | |
| 209 // Tests communication of activation and focus between Widget and | |
| 210 // NativeWidgetMus. | |
| 211 TEST_F(NativeWidgetMusTest, OnActivationChanged) { | |
| 212 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 213 widget->Show(); | |
| 214 | |
| 215 // Track activation, focus and blur events. | |
| 216 WidgetActivationObserver activation_observer(widget.get()); | |
| 217 TestWidgetFocusChangeListener focus_listener; | |
| 218 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener); | |
| 219 | |
| 220 // Deactivate the Widget, which deactivates the NativeWidgetMus. | |
| 221 widget->Deactivate(); | |
| 222 | |
| 223 // The widget is blurred and deactivated. | |
| 224 ASSERT_EQ(1u, focus_listener.focus_changes().size()); | |
| 225 EXPECT_EQ(nullptr, focus_listener.focus_changes()[0]); | |
| 226 ASSERT_EQ(1u, activation_observer.changes().size()); | |
| 227 EXPECT_EQ(false, activation_observer.changes()[0]); | |
| 228 | |
| 229 // Re-activate the Widget, which actives the NativeWidgetMus. | |
| 230 widget->Activate(); | |
| 231 | |
| 232 // The widget is focused and activated. | |
| 233 ASSERT_EQ(2u, focus_listener.focus_changes().size()); | |
| 234 EXPECT_EQ(widget->GetNativeView(), focus_listener.focus_changes()[1]); | |
| 235 ASSERT_EQ(2u, activation_observer.changes().size()); | |
| 236 EXPECT_TRUE(activation_observer.changes()[1]); | |
| 237 | |
| 238 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener); | |
| 239 } | |
| 240 | |
| 241 // Tests that showing a non-activatable widget does not activate it. | |
| 242 // TODO(jamescook): Remove this test when widget_interactive_uittests.cc runs | |
| 243 // under mus. | |
| 244 TEST_F(NativeWidgetMusTest, ShowNonActivatableWidget) { | |
| 245 Widget widget; | |
| 246 WidgetActivationObserver activation_observer(&widget); | |
| 247 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_BUBBLE); | |
| 248 params.activatable = Widget::InitParams::ACTIVATABLE_NO; | |
| 249 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 250 params.bounds = gfx::Rect(10, 20, 100, 200); | |
| 251 widget.Init(params); | |
| 252 widget.Show(); | |
| 253 | |
| 254 // The widget is not currently active. | |
| 255 EXPECT_FALSE(widget.IsActive()); | |
| 256 | |
| 257 // The widget was never active. | |
| 258 EXPECT_EQ(0u, activation_observer.changes().size()); | |
| 259 } | |
| 260 | |
| 261 // Tests that a window with app/window icons sets ui::Window icon properties. | |
| 262 TEST_F(NativeWidgetMusTest, AppAndWindowIcons) { | |
| 263 // Create a Widget with app and window icons. | |
| 264 SkBitmap app_icon_input = MakeBitmap(SK_ColorRED); | |
| 265 SkBitmap window_icon_input = MakeBitmap(SK_ColorGREEN); | |
| 266 std::unique_ptr<Widget> widget( | |
| 267 CreateWidget(new TestWidgetDelegate(app_icon_input, window_icon_input))); | |
| 268 | |
| 269 // The ui::Window has the expected icon properties. | |
| 270 ui::Window* window = | |
| 271 static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); | |
| 272 EXPECT_TRUE( | |
| 273 window->HasSharedProperty(ui::mojom::WindowManager::kAppIcon_Property)); | |
| 274 EXPECT_TRUE(window->HasSharedProperty( | |
| 275 ui::mojom::WindowManager::kWindowIcon_Property)); | |
| 276 SkBitmap app_icon = window->GetSharedProperty<SkBitmap>( | |
| 277 ui::mojom::WindowManager::kAppIcon_Property); | |
| 278 EXPECT_TRUE(gfx::BitmapsAreEqual(app_icon_input, app_icon)); | |
| 279 SkBitmap window_icon = window->GetSharedProperty<SkBitmap>( | |
| 280 ui::mojom::WindowManager::kWindowIcon_Property); | |
| 281 EXPECT_TRUE(gfx::BitmapsAreEqual(window_icon_input, window_icon)); | |
| 282 } | |
| 283 | |
| 284 // Tests that a window without icons does not set ui::Window icon properties. | |
| 285 TEST_F(NativeWidgetMusTest, NoAppNorWindowIcon) { | |
| 286 // Create a Widget without app or window icons, by supplying a null delegate. | |
| 287 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 288 | |
| 289 // The ui::Window does not have either icon property. | |
| 290 ui::Window* window = | |
| 291 static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); | |
| 292 EXPECT_FALSE( | |
| 293 window->HasSharedProperty(ui::mojom::WindowManager::kAppIcon_Property)); | |
| 294 EXPECT_FALSE(window->HasSharedProperty( | |
| 295 ui::mojom::WindowManager::kWindowIcon_Property)); | |
| 296 } | |
| 297 | |
| 298 // Tests that changing icons on a Widget updates ui::Window icon properties. | |
| 299 TEST_F(NativeWidgetMusTest, ChangeAppAndWindowIcons) { | |
| 300 // Create a Widget with app and window icons. | |
| 301 SkBitmap bitmap1 = MakeBitmap(SK_ColorRED); | |
| 302 TestWidgetDelegate* delegate = new TestWidgetDelegate(bitmap1, bitmap1); | |
| 303 std::unique_ptr<Widget> widget(CreateWidget(delegate)); | |
| 304 ui::Window* window = | |
| 305 static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); | |
| 306 | |
| 307 // Update the app icon image; verify the window has the updated icon. | |
| 308 SkBitmap bitmap2 = MakeBitmap(SK_ColorGREEN); | |
| 309 delegate->SetAppIcon(bitmap2); | |
| 310 widget->UpdateWindowIcon(); | |
| 311 SkBitmap app_icon = window->GetSharedProperty<SkBitmap>( | |
| 312 ui::mojom::WindowManager::kAppIcon_Property); | |
| 313 EXPECT_TRUE(gfx::BitmapsAreEqual(bitmap2, app_icon)); | |
| 314 | |
| 315 // Update the window icon image; verify the window has the updated icon. | |
| 316 SkBitmap bitmap3 = MakeBitmap(SK_ColorBLUE); | |
| 317 delegate->SetWindowIcon(bitmap3); | |
| 318 widget->UpdateWindowIcon(); | |
| 319 SkBitmap window_icon = window->GetSharedProperty<SkBitmap>( | |
| 320 ui::mojom::WindowManager::kWindowIcon_Property); | |
| 321 EXPECT_TRUE(gfx::BitmapsAreEqual(bitmap3, window_icon)); | |
| 322 } | |
| 323 | |
| 324 TEST_F(NativeWidgetMusTest, ValidLayerTree) { | |
| 325 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 326 View* content = new View; | |
| 327 content->SetPaintToLayer(true); | |
| 328 widget->GetContentsView()->AddChildView(content); | |
| 329 EXPECT_TRUE(widget->GetNativeWindow()->layer()->Contains(content->layer())); | |
| 330 } | |
| 331 | |
| 332 // Tests that the internal name is propagated from the Widget to the | |
| 333 // ui::Window. | |
| 334 TEST_F(NativeWidgetMusTest, GetName) { | |
| 335 Widget widget; | |
| 336 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); | |
| 337 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 338 params.name = "MyWidget"; | |
| 339 widget.Init(params); | |
| 340 ui::Window* window = | |
| 341 static_cast<NativeWidgetMus*>(widget.native_widget_private())->window(); | |
| 342 EXPECT_EQ("MyWidget", window->GetName()); | |
| 343 } | |
| 344 | |
| 345 // Tests that a Widget with a hit test mask propagates the mask to the | |
| 346 // ui::Window. | |
| 347 TEST_F(NativeWidgetMusTest, HitTestMask) { | |
| 348 gfx::Rect mask(5, 5, 10, 10); | |
| 349 std::unique_ptr<Widget> widget( | |
| 350 CreateWidget(new WidgetDelegateWithHitTestMask(mask))); | |
| 351 | |
| 352 // The window has the mask. | |
| 353 ui::Window* window = | |
| 354 static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); | |
| 355 ASSERT_TRUE(window->hit_test_mask()); | |
| 356 EXPECT_EQ(mask.ToString(), window->hit_test_mask()->ToString()); | |
| 357 } | |
| 358 | |
| 359 // Verifies changing the visibility of a child ui::Window doesn't change the | |
| 360 // visibility of the parent. | |
| 361 TEST_F(NativeWidgetMusTest, ChildVisibilityDoesntEffectParent) { | |
| 362 Widget widget; | |
| 363 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); | |
| 364 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 365 widget.Init(params); | |
| 366 widget.Show(); | |
| 367 ui::Window* window = | |
| 368 static_cast<NativeWidgetMus*>(widget.native_widget_private())->window(); | |
| 369 ASSERT_TRUE(window->visible()); | |
| 370 | |
| 371 // Create a child window, make it visible and parent it to the Widget's | |
| 372 // window. | |
| 373 ui::Window* child_window = window->window_tree()->NewWindow(); | |
| 374 child_window->SetVisible(true); | |
| 375 window->AddChild(child_window); | |
| 376 | |
| 377 // Hide the child, this should not impact the visibility of the parent. | |
| 378 child_window->SetVisible(false); | |
| 379 EXPECT_TRUE(window->visible()); | |
| 380 } | |
| 381 | |
| 382 // Tests that child aura::Windows cannot be activated. | |
| 383 TEST_F(NativeWidgetMusTest, FocusChildAuraWindow) { | |
| 384 Widget widget; | |
| 385 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); | |
| 386 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 387 widget.Init(params); | |
| 388 | |
| 389 View* focusable = new View; | |
| 390 focusable->SetFocusBehavior(View::FocusBehavior::ALWAYS); | |
| 391 widget.GetContentsView()->AddChildView(focusable); | |
| 392 | |
| 393 NativeViewHost* native_host = new NativeViewHost; | |
| 394 widget.GetContentsView()->AddChildView(native_host); | |
| 395 | |
| 396 std::unique_ptr<aura::Window> window(new aura::Window(nullptr)); | |
| 397 window->Init(ui::LayerType::LAYER_SOLID_COLOR); | |
| 398 native_host->SetBounds(5, 10, 20, 30); | |
| 399 native_host->Attach(window.get()); | |
| 400 widget.Show(); | |
| 401 window->Show(); | |
| 402 widget.SetBounds(gfx::Rect(10, 20, 30, 40)); | |
| 403 | |
| 404 // Sanity check that the |window| is a descendent of the Widget's window. | |
| 405 ASSERT_TRUE(widget.GetNativeView()->Contains(window->parent())); | |
| 406 | |
| 407 // Focusing the child window should not activate it. | |
| 408 window->Focus(); | |
| 409 EXPECT_TRUE(window->HasFocus()); | |
| 410 aura::Window* active_window = | |
| 411 aura::client::GetActivationClient(window.get()->GetRootWindow()) | |
| 412 ->GetActiveWindow(); | |
| 413 EXPECT_NE(window.get(), active_window); | |
| 414 EXPECT_EQ(widget.GetNativeView(), active_window); | |
| 415 | |
| 416 // Moving focus to a child View should move focus away from |window|, and to | |
| 417 // the Widget's window instead. | |
| 418 focusable->RequestFocus(); | |
| 419 EXPECT_FALSE(window->HasFocus()); | |
| 420 EXPECT_TRUE(widget.GetNativeView()->HasFocus()); | |
| 421 active_window = | |
| 422 aura::client::GetActivationClient(window.get()->GetRootWindow()) | |
| 423 ->GetActiveWindow(); | |
| 424 EXPECT_EQ(widget.GetNativeView(), active_window); | |
| 425 } | |
| 426 | |
| 427 TEST_F(NativeWidgetMusTest, WidgetReceivesEvent) { | |
| 428 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 429 widget->Show(); | |
| 430 | |
| 431 View* content = new HandleMousePressView; | |
| 432 content->SetBounds(10, 20, 90, 180); | |
| 433 widget->GetContentsView()->AddChildView(content); | |
| 434 | |
| 435 ui::test::TestEventHandler handler; | |
| 436 content->AddPreTargetHandler(&handler); | |
| 437 | |
| 438 std::unique_ptr<ui::MouseEvent> mouse = CreateMouseEvent(); | |
| 439 NativeWidgetMus* native_widget = | |
| 440 static_cast<NativeWidgetMus*>(widget->native_widget_private()); | |
| 441 ui::WindowTreeClientPrivate test_api(native_widget->window()); | |
| 442 test_api.CallOnWindowInputEvent(native_widget->window(), std::move(mouse)); | |
| 443 EXPECT_EQ(1, handler.num_mouse_events()); | |
| 444 } | |
| 445 | |
| 446 // Tests that an incoming UI event is acked with the handled status. | |
| 447 TEST_F(NativeWidgetMusTest, EventAcked) { | |
| 448 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 449 widget->Show(); | |
| 450 | |
| 451 View* content = new HandleMousePressView; | |
| 452 content->SetBounds(10, 20, 90, 180); | |
| 453 widget->GetContentsView()->AddChildView(content); | |
| 454 | |
| 455 // Dispatch an input event to the window and view. | |
| 456 std::unique_ptr<ui::MouseEvent> event = CreateMouseEvent(); | |
| 457 std::unique_ptr<base::Callback<void(EventResult)>> ack_callback( | |
| 458 new base::Callback<void(EventResult)>(base::Bind( | |
| 459 &NativeWidgetMusTest::AckCallback, base::Unretained(this)))); | |
| 460 OnWindowInputEvent( | |
| 461 static_cast<NativeWidgetMus*>(widget->native_widget_private()), | |
| 462 *event, | |
| 463 &ack_callback); | |
| 464 | |
| 465 // The test took ownership of the callback and called it. | |
| 466 EXPECT_FALSE(ack_callback); | |
| 467 EXPECT_EQ(1, ack_callback_count()); | |
| 468 } | |
| 469 | |
| 470 // Tests that a window that is deleted during event handling properly acks the | |
| 471 // event. | |
| 472 TEST_F(NativeWidgetMusTest, EventAckedWithWindowDestruction) { | |
| 473 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 474 widget->Show(); | |
| 475 | |
| 476 View* content = new DeleteWidgetView(&widget); | |
| 477 content->SetBounds(10, 20, 90, 180); | |
| 478 widget->GetContentsView()->AddChildView(content); | |
| 479 | |
| 480 // Dispatch an input event to the window and view. | |
| 481 std::unique_ptr<ui::MouseEvent> event = CreateMouseEvent(); | |
| 482 std::unique_ptr<base::Callback<void(EventResult)>> ack_callback( | |
| 483 new base::Callback<void(EventResult)>(base::Bind( | |
| 484 &NativeWidgetMusTest::AckCallback, base::Unretained(this)))); | |
| 485 OnWindowInputEvent( | |
| 486 static_cast<NativeWidgetMus*>(widget->native_widget_private()), | |
| 487 *event, | |
| 488 &ack_callback); | |
| 489 | |
| 490 // The widget was deleted. | |
| 491 EXPECT_FALSE(widget.get()); | |
| 492 | |
| 493 // The test took ownership of the callback and called it. | |
| 494 EXPECT_FALSE(ack_callback); | |
| 495 EXPECT_EQ(1, ack_callback_count()); | |
| 496 } | |
| 497 | |
| 498 TEST_F(NativeWidgetMusTest, SetAndReleaseCapture) { | |
| 499 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 500 widget->Show(); | |
| 501 View* content = new View; | |
| 502 widget->GetContentsView()->AddChildView(content); | |
| 503 internal::NativeWidgetPrivate* widget_private = | |
| 504 widget->native_widget_private(); | |
| 505 ui::Window* mus_window = | |
| 506 static_cast<NativeWidgetMus*>(widget_private)->window(); | |
| 507 EXPECT_FALSE(widget_private->HasCapture()); | |
| 508 EXPECT_FALSE(mus_window->HasCapture()); | |
| 509 | |
| 510 widget->SetCapture(content); | |
| 511 EXPECT_TRUE(widget_private->HasCapture()); | |
| 512 EXPECT_TRUE(mus_window->HasCapture()); | |
| 513 | |
| 514 widget->ReleaseCapture(); | |
| 515 EXPECT_FALSE(widget_private->HasCapture()); | |
| 516 EXPECT_FALSE(mus_window->HasCapture()); | |
| 517 } | |
| 518 | |
| 519 // Ensure that manually setting NativeWidgetMus's ui::Window bounds also | |
| 520 // updates its WindowTreeHost bounds. | |
| 521 TEST_F(NativeWidgetMusTest, SetMusWindowBounds) { | |
| 522 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 523 widget->Show(); | |
| 524 View* content = new View; | |
| 525 widget->GetContentsView()->AddChildView(content); | |
| 526 NativeWidgetMus* native_widget = | |
| 527 static_cast<NativeWidgetMus*>(widget->native_widget_private()); | |
| 528 ui::Window* mus_window = native_widget->window(); | |
| 529 | |
| 530 gfx::Rect start_bounds = initial_bounds(); | |
| 531 gfx::Rect end_bounds = gfx::Rect(40, 50, 60, 70); | |
| 532 EXPECT_NE(start_bounds, end_bounds); | |
| 533 | |
| 534 EXPECT_EQ(start_bounds, mus_window->bounds()); | |
| 535 EXPECT_EQ(start_bounds, | |
| 536 native_widget->window_tree_host()->GetBoundsInPixels()); | |
| 537 | |
| 538 mus_window->SetBounds(end_bounds); | |
| 539 | |
| 540 EXPECT_EQ(end_bounds, mus_window->bounds()); | |
| 541 | |
| 542 // Main check for this test: Setting |mus_window| bounds while bypassing | |
| 543 // |native_widget| must update window_tree_host bounds. | |
| 544 EXPECT_EQ(end_bounds, native_widget->window_tree_host()->GetBoundsInPixels()); | |
| 545 } | |
| 546 | |
| 547 // Verifies visibility of the aura::Window and ui::Window are updated when the | |
| 548 // Widget is shown/hidden. | |
| 549 TEST_F(NativeWidgetMusTest, TargetVisibility) { | |
| 550 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 551 NativeWidgetMus* native_widget = | |
| 552 static_cast<NativeWidgetMus*>(widget->native_widget_private()); | |
| 553 ui::Window* mus_window = native_widget->window(); | |
| 554 EXPECT_FALSE(mus_window->visible()); | |
| 555 EXPECT_FALSE(widget->GetNativeView()->TargetVisibility()); | |
| 556 | |
| 557 widget->Show(); | |
| 558 EXPECT_TRUE(mus_window->visible()); | |
| 559 EXPECT_TRUE(widget->GetNativeView()->TargetVisibility()); | |
| 560 } | |
| 561 | |
| 562 // Indirectly verifies Show() isn't invoked twice on the underlying | |
| 563 // aura::Window. | |
| 564 TEST_F(NativeWidgetMusTest, DontShowTwice) { | |
| 565 std::unique_ptr<Widget> widget(CreateWidget(nullptr)); | |
| 566 widget->GetNativeView()->layer()->SetOpacity(0.0f); | |
| 567 // aura::Window::Show() allows the opacity to be 0 as long as the window is | |
| 568 // hidden. So, as long as this only invokes aura::Window::Show() once the | |
| 569 // DCHECK in aura::Window::Show() won't fire. | |
| 570 widget->Show(); | |
| 571 } | |
| 572 | |
| 573 namespace { | |
| 574 | |
| 575 // See description of test for details. | |
| 576 class IsMaximizedObserver : public ui::WindowObserver { | |
| 577 public: | |
| 578 IsMaximizedObserver() {} | |
| 579 ~IsMaximizedObserver() override {} | |
| 580 | |
| 581 void set_widget(Widget* widget) { widget_ = widget; } | |
| 582 | |
| 583 bool got_change() const { return got_change_; } | |
| 584 | |
| 585 // ui::WindowObserver: | |
| 586 void OnWindowSharedPropertyChanged( | |
| 587 ui::Window* window, | |
| 588 const std::string& name, | |
| 589 const std::vector<uint8_t>* old_data, | |
| 590 const std::vector<uint8_t>* new_data) override { | |
| 591 // Expect only one change for the show state. | |
| 592 ASSERT_FALSE(got_change_); | |
| 593 got_change_ = true; | |
| 594 EXPECT_EQ(ui::mojom::WindowManager::kShowState_Property, name); | |
| 595 EXPECT_TRUE(widget_->IsMaximized()); | |
| 596 } | |
| 597 | |
| 598 private: | |
| 599 bool got_change_ = false; | |
| 600 Widget* widget_ = nullptr; | |
| 601 | |
| 602 DISALLOW_COPY_AND_ASSIGN(IsMaximizedObserver); | |
| 603 }; | |
| 604 | |
| 605 } // namespace | |
| 606 | |
| 607 // Verifies that asking for Widget::IsMaximized() from within | |
| 608 // OnWindowSharedPropertyChanged() returns the right thing. | |
| 609 TEST_F(NativeWidgetMusTest, IsMaximized) { | |
| 610 ASSERT_TRUE(WindowManagerConnection::Exists()); | |
| 611 ui::Window* window = WindowManagerConnection::Get()->NewTopLevelWindow( | |
| 612 std::map<std::string, std::vector<uint8_t>>()); | |
| 613 IsMaximizedObserver observer; | |
| 614 // NOTE: the order here is important, we purposefully add the | |
| 615 // ui::WindowObserver before creating NativeWidgetMus, which also adds its | |
| 616 // own observer. | |
| 617 window->AddObserver(&observer); | |
| 618 | |
| 619 std::unique_ptr<Widget> widget(new Widget()); | |
| 620 observer.set_widget(widget.get()); | |
| 621 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); | |
| 622 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 623 params.bounds = initial_bounds(); | |
| 624 params.native_widget = new NativeWidgetMus( | |
| 625 widget.get(), window, ui::mojom::CompositorFrameSinkType::DEFAULT); | |
| 626 widget->Init(params); | |
| 627 window->SetSharedProperty<aura::PropertyConverter::PrimitiveType>( | |
| 628 ui::mojom::WindowManager::kShowState_Property, | |
| 629 static_cast<aura::PropertyConverter::PrimitiveType>( | |
| 630 ui::mojom::ShowState::MAXIMIZED)); | |
| 631 EXPECT_TRUE(widget->IsMaximized()); | |
| 632 } | |
| 633 | |
| 634 // This test is to ensure that when initializing a widget with InitParams.parent | |
| 635 // set to another widget's aura::Window, the ui::Window of the former widget is | |
| 636 // added as a child to the ui::Window of the latter widget. | |
| 637 TEST_F(NativeWidgetMusTest, InitNativeWidgetParentsUIWindow) { | |
| 638 ASSERT_TRUE(WindowManagerConnection::Exists()); | |
| 639 | |
| 640 ui::Window* parent_window = WindowManagerConnection::Get()->NewTopLevelWindow( | |
| 641 std::map<std::string, std::vector<uint8_t>>()); | |
| 642 std::unique_ptr<Widget> parent_widget(new Widget()); | |
| 643 Widget::InitParams parent_params = | |
| 644 CreateParams(Widget::InitParams::TYPE_WINDOW); | |
| 645 parent_params.name = "Parent Widget"; | |
| 646 parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 647 parent_params.shadow_type = Widget::InitParams::SHADOW_TYPE_NONE; | |
| 648 parent_params.opacity = Widget::InitParams::OPAQUE_WINDOW; | |
| 649 parent_params.parent = nullptr; | |
| 650 parent_params.bounds = initial_bounds(); | |
| 651 parent_params.native_widget = | |
| 652 new NativeWidgetMus(parent_widget.get(), parent_window, | |
| 653 ui::mojom::CompositorFrameSinkType::DEFAULT); | |
| 654 parent_widget->Init(parent_params); | |
| 655 | |
| 656 std::unique_ptr<Widget> child_widget(new Widget()); | |
| 657 ui::Window* child_window = parent_window->window_tree()->NewWindow(); | |
| 658 Widget::InitParams child_params = CreateParams(Widget::InitParams::TYPE_MENU); | |
| 659 child_params.parent = parent_widget->GetNativeView(); | |
| 660 child_params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 661 child_params.name = "Child Widget"; | |
| 662 child_params.native_widget = | |
| 663 new NativeWidgetMus(child_widget.get(), child_window, | |
| 664 ui::mojom::CompositorFrameSinkType::DEFAULT); | |
| 665 child_widget->Init(child_params); | |
| 666 | |
| 667 EXPECT_EQ(child_window->parent(), parent_window); | |
| 668 | |
| 669 std::unique_ptr<Widget> not_child_widget(new Widget()); | |
| 670 ui::Window* not_child_window = parent_window->window_tree()->NewWindow(); | |
| 671 Widget::InitParams not_child_params = | |
| 672 CreateParams(Widget::InitParams::TYPE_MENU); | |
| 673 not_child_params.ownership = | |
| 674 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 675 not_child_params.name = "Not Child Widget"; | |
| 676 not_child_params.native_widget = | |
| 677 new NativeWidgetMus(not_child_widget.get(), not_child_window, | |
| 678 ui::mojom::CompositorFrameSinkType::DEFAULT); | |
| 679 not_child_widget->Init(not_child_params); | |
| 680 | |
| 681 EXPECT_NE(not_child_window->parent(), parent_window); | |
| 682 } | |
| 683 | |
| 684 } // namespace views | |
| OLD | NEW |