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 |