1 // Copyright (c) 2012 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 "ui/views/widget/native_widget_aura.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/command_line.h"
11 #include "base/macros.h"
12 #include "base/run_loop.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "ui/aura/client/aura_constants.h"
15 #include "ui/aura/env.h"
16 #include "ui/aura/layout_manager.h"
17 #include "ui/aura/window.h"
18 #include "ui/aura/window_observer.h"
19 #include "ui/aura/window_tree_host.h"
20 #include "ui/events/event.h"
21 #include "ui/events/event_utils.h"
22 #include "ui/views/layout/fill_layout.h"
23 #include "ui/views/test/views_test_base.h"
24 #include "ui/views/test/widget_test.h"
25 #include "ui/views/widget/root_view.h"
26 #include "ui/views/widget/widget_delegate.h"
27 #include "ui/wm/core/base_focus_rules.h"
28 #include "ui/wm/core/default_activation_client.h"
29 #include "ui/wm/core/focus_controller.h"
30 #include "ui/wm/core/transient_window_manager.h"
31
32 namespace views {
33 namespace {
34
Init(aura::Window * parent,Widget * widget)35 NativeWidgetAura* Init(aura::Window* parent, Widget* widget) {
36 Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
37 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
38 params.parent = parent;
39 widget->Init(std::move(params));
40 return static_cast<NativeWidgetAura*>(widget->native_widget());
41 }
42
43 // TestFocusRules is intended to provide a way to manually set a window's
44 // activatability so that the focus rules can be tested.
45 class TestFocusRules : public wm::BaseFocusRules {
46 public:
47 TestFocusRules() = default;
48 ~TestFocusRules() override = default;
49
set_can_activate(bool can_activate)50 void set_can_activate(bool can_activate) { can_activate_ = can_activate; }
51
52 // wm::BaseFocusRules overrides:
SupportsChildActivation(const aura::Window * window) const53 bool SupportsChildActivation(const aura::Window* window) const override {
54 return true;
55 }
56
CanActivateWindow(const aura::Window * window) const57 bool CanActivateWindow(const aura::Window* window) const override {
58 return can_activate_;
59 }
60
61 private:
62 bool can_activate_ = true;
63
64 DISALLOW_COPY_AND_ASSIGN(TestFocusRules);
65 };
66
67 class NativeWidgetAuraTest : public ViewsTestBase {
68 public:
69 NativeWidgetAuraTest() = default;
70 ~NativeWidgetAuraTest() override = default;
71
test_focus_rules()72 TestFocusRules* test_focus_rules() { return test_focus_rules_; }
73
74 // testing::Test overrides:
SetUp()75 void SetUp() override {
76 ViewsTestBase::SetUp();
77 test_focus_rules_ = new TestFocusRules;
78 focus_controller_ =
79 std::make_unique<wm::FocusController>(test_focus_rules_);
80 wm::SetActivationClient(root_window(), focus_controller_.get());
81 host()->SetBoundsInPixels(gfx::Rect(640, 480));
82 }
83
84 private:
85 std::unique_ptr<wm::FocusController> focus_controller_;
86 TestFocusRules* test_focus_rules_;
87
88 DISALLOW_COPY_AND_ASSIGN(NativeWidgetAuraTest);
89 };
90
TEST_F(NativeWidgetAuraTest,CenterWindowLargeParent)91 TEST_F(NativeWidgetAuraTest, CenterWindowLargeParent) {
92 // Make a parent window larger than the host represented by
93 // WindowEventDispatcher.
94 std::unique_ptr<aura::Window> parent(new aura::Window(nullptr));
95 parent->Init(ui::LAYER_NOT_DRAWN);
96 parent->SetBounds(gfx::Rect(0, 0, 1024, 800));
97 std::unique_ptr<Widget> widget(new Widget());
98 NativeWidgetAura* window = Init(parent.get(), widget.get());
99
100 window->CenterWindow(gfx::Size(100, 100));
101 EXPECT_EQ(gfx::Rect((640 - 100) / 2, (480 - 100) / 2, 100, 100),
102 window->GetNativeWindow()->bounds());
103 widget->CloseNow();
104 }
105
TEST_F(NativeWidgetAuraTest,CenterWindowSmallParent)106 TEST_F(NativeWidgetAuraTest, CenterWindowSmallParent) {
107 // Make a parent window smaller than the host represented by
108 // WindowEventDispatcher.
109 std::unique_ptr<aura::Window> parent(new aura::Window(nullptr));
110 parent->Init(ui::LAYER_NOT_DRAWN);
111 parent->SetBounds(gfx::Rect(0, 0, 480, 320));
112 std::unique_ptr<Widget> widget(new Widget());
113 NativeWidgetAura* window = Init(parent.get(), widget.get());
114
115 window->CenterWindow(gfx::Size(100, 100));
116 EXPECT_EQ(gfx::Rect((480 - 100) / 2, (320 - 100) / 2, 100, 100),
117 window->GetNativeWindow()->bounds());
118 widget->CloseNow();
119 }
120
121 // Verifies CenterWindow() constrains to parent size.
TEST_F(NativeWidgetAuraTest,CenterWindowSmallParentNotAtOrigin)122 TEST_F(NativeWidgetAuraTest, CenterWindowSmallParentNotAtOrigin) {
123 // Make a parent window smaller than the host represented by
124 // WindowEventDispatcher and offset it slightly from the origin.
125 std::unique_ptr<aura::Window> parent(new aura::Window(nullptr));
126 parent->Init(ui::LAYER_NOT_DRAWN);
127 parent->SetBounds(gfx::Rect(20, 40, 480, 320));
128 std::unique_ptr<Widget> widget(new Widget());
129 NativeWidgetAura* window = Init(parent.get(), widget.get());
130 window->CenterWindow(gfx::Size(500, 600));
131
132 // |window| should be no bigger than |parent|.
133 EXPECT_EQ("20,40 480x320", window->GetNativeWindow()->bounds().ToString());
134 widget->CloseNow();
135 }
136
TEST_F(NativeWidgetAuraTest,CreateMinimized)137 TEST_F(NativeWidgetAuraTest, CreateMinimized) {
138 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
139 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
140 params.parent = nullptr;
141 params.context = root_window();
142 params.show_state = ui::SHOW_STATE_MINIMIZED;
143 params.bounds.SetRect(0, 0, 1024, 800);
144 std::unique_ptr<Widget> widget(new Widget());
145 widget->Init(std::move(params));
146 widget->Show();
147
148 EXPECT_TRUE(widget->IsMinimized());
149 widget->CloseNow();
150 }
151
152 // Tests that GetRestoreBounds returns the window bounds even if the window is
153 // transformed.
TEST_F(NativeWidgetAuraTest,RestoreBounds)154 TEST_F(NativeWidgetAuraTest, RestoreBounds) {
155 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
156 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
157 params.parent = nullptr;
158 params.context = root_window();
159 params.bounds.SetRect(0, 0, 400, 400);
160 auto widget = std::make_unique<Widget>();
161 widget->Init(std::move(params));
162 widget->Show();
163 EXPECT_EQ(gfx::Rect(400, 400), widget->GetRestoredBounds());
164
165 gfx::Transform transform;
166 transform.Translate(100.f, 100.f);
167 widget->GetNativeWindow()->SetTransform(transform);
168 EXPECT_EQ(gfx::Rect(400, 400), widget->GetRestoredBounds());
169 }
170
171 // A WindowObserver that counts kShowStateKey property changes.
172 class TestWindowObserver : public aura::WindowObserver {
173 public:
TestWindowObserver(gfx::NativeWindow window)174 explicit TestWindowObserver(gfx::NativeWindow window) : window_(window) {
175 window_->AddObserver(this);
176 }
~TestWindowObserver()177 ~TestWindowObserver() override { window_->RemoveObserver(this); }
178
179 // aura::WindowObserver:
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)180 void OnWindowPropertyChanged(aura::Window* window,
181 const void* key,
182 intptr_t old) override {
183 if (key != aura::client::kShowStateKey)
184 return;
185 count_++;
186 state_ = window_->GetProperty(aura::client::kShowStateKey);
187 }
188
count() const189 int count() const { return count_; }
state() const190 ui::WindowShowState state() const { return state_; }
Reset()191 void Reset() { count_ = 0; }
192
193 private:
194 gfx::NativeWindow window_;
195 int count_ = 0;
196 ui::WindowShowState state_ = ui::WindowShowState::SHOW_STATE_DEFAULT;
197
198 DISALLOW_COPY_AND_ASSIGN(TestWindowObserver);
199 };
200
201 // Tests that window transitions from normal to minimized and back do not
202 // involve extra show state transitions.
TEST_F(NativeWidgetAuraTest,ToggleState)203 TEST_F(NativeWidgetAuraTest, ToggleState) {
204 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
205 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
206 params.parent = nullptr;
207 params.context = root_window();
208 params.show_state = ui::SHOW_STATE_NORMAL;
209 params.bounds.SetRect(0, 0, 1024, 800);
210 Widget widget;
211 widget.Init(std::move(params));
212 std::unique_ptr<TestWindowObserver> observer(
213 new TestWindowObserver(widget.GetNativeWindow()));
214 widget.Show();
215 EXPECT_FALSE(widget.IsMinimized());
216 EXPECT_EQ(0, observer->count());
217 EXPECT_EQ(ui::WindowShowState::SHOW_STATE_DEFAULT, observer->state());
218
219 widget.Minimize();
220 EXPECT_TRUE(widget.IsMinimized());
221 EXPECT_EQ(1, observer->count());
222 EXPECT_EQ(ui::WindowShowState::SHOW_STATE_MINIMIZED, observer->state());
223 observer->Reset();
224
225 widget.Show();
226 widget.Restore();
227 EXPECT_EQ(1, observer->count());
228 EXPECT_EQ(ui::WindowShowState::SHOW_STATE_NORMAL, observer->state());
229
230 observer.reset();
231 EXPECT_FALSE(widget.IsMinimized());
232 widget.CloseNow();
233 }
234
235 class TestLayoutManagerBase : public aura::LayoutManager {
236 public:
237 TestLayoutManagerBase() = default;
238 ~TestLayoutManagerBase() override = default;
239
240 // aura::LayoutManager:
OnWindowResized()241 void OnWindowResized() override {}
OnWindowAddedToLayout(aura::Window * child)242 void OnWindowAddedToLayout(aura::Window* child) override {}
OnWillRemoveWindowFromLayout(aura::Window * child)243 void OnWillRemoveWindowFromLayout(aura::Window* child) override {}
OnWindowRemovedFromLayout(aura::Window * child)244 void OnWindowRemovedFromLayout(aura::Window* child) override {}
OnChildWindowVisibilityChanged(aura::Window * child,bool visible)245 void OnChildWindowVisibilityChanged(aura::Window* child,
246 bool visible) override {}
SetChildBounds(aura::Window * child,const gfx::Rect & requested_bounds)247 void SetChildBounds(aura::Window* child,
248 const gfx::Rect& requested_bounds) override {}
249
250 private:
251 DISALLOW_COPY_AND_ASSIGN(TestLayoutManagerBase);
252 };
253
254 // Used by ShowMaximizedDoesntBounceAround. See it for details.
255 class MaximizeLayoutManager : public TestLayoutManagerBase {
256 public:
257 MaximizeLayoutManager() = default;
258 ~MaximizeLayoutManager() override = default;
259
260 private:
261 // aura::LayoutManager:
OnWindowAddedToLayout(aura::Window * child)262 void OnWindowAddedToLayout(aura::Window* child) override {
263 // This simulates what happens when adding a maximized window.
264 SetChildBoundsDirect(child, gfx::Rect(0, 0, 300, 300));
265 }
266
267 DISALLOW_COPY_AND_ASSIGN(MaximizeLayoutManager);
268 };
269
270 // This simulates BrowserView, which creates a custom RootView so that
271 // OnNativeWidgetSizeChanged that is invoked during Init matters.
272 class TestWidget : public Widget {
273 public:
274 TestWidget() = default;
275
276 // Returns true if the size changes to a non-empty size, and then to another
277 // size.
did_size_change_more_than_once() const278 bool did_size_change_more_than_once() const {
279 return did_size_change_more_than_once_;
280 }
281
OnNativeWidgetSizeChanged(const gfx::Size & new_size)282 void OnNativeWidgetSizeChanged(const gfx::Size& new_size) override {
283 if (last_size_.IsEmpty())
284 last_size_ = new_size;
285 else if (!did_size_change_more_than_once_ && new_size != last_size_)
286 did_size_change_more_than_once_ = true;
287 Widget::OnNativeWidgetSizeChanged(new_size);
288 }
289
290 private:
291 bool did_size_change_more_than_once_ = false;
292 gfx::Size last_size_;
293
294 DISALLOW_COPY_AND_ASSIGN(TestWidget);
295 };
296
297 // Verifies the size of the widget doesn't change more than once during Init if
298 // the window ends up maximized. This is important as otherwise
299 // RenderWidgetHostViewAura ends up getting resized during construction, which
300 // leads to noticable flashes.
TEST_F(NativeWidgetAuraTest,ShowMaximizedDoesntBounceAround)301 TEST_F(NativeWidgetAuraTest, ShowMaximizedDoesntBounceAround) {
302 root_window()->SetBounds(gfx::Rect(0, 0, 640, 480));
303 root_window()->SetLayoutManager(new MaximizeLayoutManager);
304 std::unique_ptr<TestWidget> widget(new TestWidget());
305 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
306 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
307 params.parent = nullptr;
308 params.context = root_window();
309 params.show_state = ui::SHOW_STATE_MAXIMIZED;
310 params.bounds = gfx::Rect(10, 10, 100, 200);
311 widget->Init(std::move(params));
312 EXPECT_FALSE(widget->did_size_change_more_than_once());
313 widget->CloseNow();
314 }
315
316 class PropertyTestLayoutManager : public TestLayoutManagerBase {
317 public:
318 PropertyTestLayoutManager() = default;
319 ~PropertyTestLayoutManager() override = default;
320
added() const321 bool added() const { return added_; }
322
323 private:
324 // aura::LayoutManager:
OnWindowAddedToLayout(aura::Window * child)325 void OnWindowAddedToLayout(aura::Window* child) override {
326 EXPECT_EQ(aura::client::kResizeBehaviorCanResize |
327 aura::client::kResizeBehaviorCanMaximize |
328 aura::client::kResizeBehaviorCanMinimize,
329 child->GetProperty(aura::client::kResizeBehaviorKey));
330 added_ = true;
331 }
332
333 bool added_ = false;
334
335 DISALLOW_COPY_AND_ASSIGN(PropertyTestLayoutManager);
336 };
337
338 // Verifies the resize behavior when added to the layout manager.
TEST_F(NativeWidgetAuraTest,TestPropertiesWhenAddedToLayout)339 TEST_F(NativeWidgetAuraTest, TestPropertiesWhenAddedToLayout) {
340 root_window()->SetBounds(gfx::Rect(0, 0, 640, 480));
341 PropertyTestLayoutManager* layout_manager = new PropertyTestLayoutManager();
342 root_window()->SetLayoutManager(layout_manager);
343 std::unique_ptr<TestWidget> widget(new TestWidget());
344 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
345 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
346 params.delegate = new WidgetDelegate();
347 params.delegate->SetOwnedByWidget(true);
348 params.delegate->SetHasWindowSizeControls(true);
349 params.parent = nullptr;
350 params.context = root_window();
351 widget->Init(std::move(params));
352 EXPECT_TRUE(layout_manager->added());
353 widget->CloseNow();
354 }
355
TEST_F(NativeWidgetAuraTest,GetClientAreaScreenBounds)356 TEST_F(NativeWidgetAuraTest, GetClientAreaScreenBounds) {
357 // Create a widget.
358 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
359 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
360 params.context = root_window();
361 params.bounds.SetRect(10, 20, 300, 400);
362 std::unique_ptr<Widget> widget(new Widget());
363 widget->Init(std::move(params));
364
365 // For Aura, client area bounds match window bounds.
366 gfx::Rect client_bounds = widget->GetClientAreaBoundsInScreen();
367 EXPECT_EQ(10, client_bounds.x());
368 EXPECT_EQ(20, client_bounds.y());
369 EXPECT_EQ(300, client_bounds.width());
370 EXPECT_EQ(400, client_bounds.height());
371 }
372
373 // View subclass that tracks whether it has gotten a gesture event.
374 class GestureTrackingView : public View {
375 public:
376 GestureTrackingView() = default;
377
set_consume_gesture_event(bool value)378 void set_consume_gesture_event(bool value) { consume_gesture_event_ = value; }
379
clear_got_gesture_event()380 void clear_got_gesture_event() { got_gesture_event_ = false; }
got_gesture_event() const381 bool got_gesture_event() const { return got_gesture_event_; }
382
383 // View overrides:
OnGestureEvent(ui::GestureEvent * event)384 void OnGestureEvent(ui::GestureEvent* event) override {
385 got_gesture_event_ = true;
386 if (consume_gesture_event_)
387 event->StopPropagation();
388 }
389
390 private:
391 // Was OnGestureEvent() invoked?
392 bool got_gesture_event_ = false;
393
394 // Dictates what OnGestureEvent() returns.
395 bool consume_gesture_event_ = true;
396
397 DISALLOW_COPY_AND_ASSIGN(GestureTrackingView);
398 };
399
400 // Verifies a capture isn't set on touch press and that the view that gets
401 // the press gets the release.
TEST_F(NativeWidgetAuraTest,DontCaptureOnGesture)402 TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) {
403 // Create two views (both sized the same). |child| is configured not to
404 // consume the gesture event.
405 auto content_view = std::make_unique<GestureTrackingView>();
406 GestureTrackingView* child = new GestureTrackingView();
407 child->set_consume_gesture_event(false);
408 content_view->SetLayoutManager(std::make_unique<FillLayout>());
409 content_view->AddChildView(child);
410 std::unique_ptr<TestWidget> widget(new TestWidget());
411 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
412 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
413 params.context = root_window();
414 params.bounds = gfx::Rect(0, 0, 100, 200);
415 widget->Init(std::move(params));
416 GestureTrackingView* view = widget->SetContentsView(std::move(content_view));
417 widget->Show();
418
419 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(41, 51),
420 ui::EventTimeForNow(),
421 ui::PointerDetails(ui::EventPointerType::kTouch, 1));
422 ui::EventDispatchDetails details = event_sink()->OnEventFromSource(&press);
423 ASSERT_FALSE(details.dispatcher_destroyed);
424 // Both views should get the press.
425 EXPECT_TRUE(view->got_gesture_event());
426 EXPECT_TRUE(child->got_gesture_event());
427 view->clear_got_gesture_event();
428 child->clear_got_gesture_event();
429 // Touch events should not automatically grab capture.
430 EXPECT_FALSE(widget->HasCapture());
431
432 // Release touch. Only |view| should get the release since that it consumed
433 // the press.
434 ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(250, 251),
435 ui::EventTimeForNow(),
436 ui::PointerDetails(ui::EventPointerType::kTouch, 1));
437 details = event_sink()->OnEventFromSource(&release);
438 ASSERT_FALSE(details.dispatcher_destroyed);
439 EXPECT_TRUE(view->got_gesture_event());
440 EXPECT_FALSE(child->got_gesture_event());
441 view->clear_got_gesture_event();
442 }
443
444 // Verifies views with layers are targeted for events properly.
TEST_F(NativeWidgetAuraTest,PreferViewLayersToChildWindows)445 TEST_F(NativeWidgetAuraTest, PreferViewLayersToChildWindows) {
446 // Create two widgets: |parent| and |child|. |child| is a child of |parent|.
447 std::unique_ptr<Widget> parent(new Widget());
448 Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
449 parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
450 parent_params.context = root_window();
451 parent->Init(std::move(parent_params));
452 View* parent_root = parent->SetContentsView(std::make_unique<View>());
453 parent->SetBounds(gfx::Rect(0, 0, 400, 400));
454 parent->Show();
455
456 std::unique_ptr<Widget> child(new Widget());
457 Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL);
458 child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
459 child_params.parent = parent->GetNativeWindow();
460 child->Init(std::move(child_params));
461 child->SetBounds(gfx::Rect(0, 0, 200, 200));
462 child->Show();
463
464 // Point is over |child|.
465 EXPECT_EQ(
466 child->GetNativeWindow(),
467 parent->GetNativeWindow()->GetEventHandlerForPoint(gfx::Point(50, 50)));
468
469 // Create a view with a layer and stack it at the bottom (below |child|).
470 View* view_with_layer = new View;
471 parent_root->AddChildView(view_with_layer);
472 view_with_layer->SetBounds(0, 0, 50, 50);
473 view_with_layer->SetPaintToLayer();
474
475 // Make sure that |child| still gets the event.
476 EXPECT_EQ(
477 child->GetNativeWindow(),
478 parent->GetNativeWindow()->GetEventHandlerForPoint(gfx::Point(20, 20)));
479
480 // Move |view_with_layer| to the top and make sure it gets the
481 // event when the point is within |view_with_layer|'s bounds.
482 view_with_layer->layer()->parent()->StackAtTop(view_with_layer->layer());
483 EXPECT_EQ(
484 parent->GetNativeWindow(),
485 parent->GetNativeWindow()->GetEventHandlerForPoint(gfx::Point(20, 20)));
486
487 // Point is over |child|, it should get the event.
488 EXPECT_EQ(
489 child->GetNativeWindow(),
490 parent->GetNativeWindow()->GetEventHandlerForPoint(gfx::Point(70, 70)));
491
492 delete view_with_layer;
493 view_with_layer = nullptr;
494
495 EXPECT_EQ(
496 child->GetNativeWindow(),
497 parent->GetNativeWindow()->GetEventHandlerForPoint(gfx::Point(20, 20)));
498 }
499
500 // Verifies views with layers are targeted for events properly.
TEST_F(NativeWidgetAuraTest,ShouldDescendIntoChildForEventHandlingChecksVisibleBounds)501 TEST_F(NativeWidgetAuraTest,
502 ShouldDescendIntoChildForEventHandlingChecksVisibleBounds) {
503 // Create two widgets: |parent| and |child|. |child| is a child of |parent|.
504 Widget parent;
505 Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
506 parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
507 parent_params.context = root_window();
508 parent.Init(std::move(parent_params));
509 View* parent_root_view = parent.SetContentsView(std::make_unique<View>());
510 parent.SetBounds(gfx::Rect(0, 0, 400, 400));
511 parent.Show();
512
513 Widget child;
514 Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL);
515 child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
516 child_params.parent = parent.GetNativeWindow();
517 child.Init(std::move(child_params));
518 child.SetBounds(gfx::Rect(0, 0, 200, 200));
519 child.Show();
520
521 // Point is over |child|.
522 EXPECT_EQ(
523 child.GetNativeWindow(),
524 parent.GetNativeWindow()->GetEventHandlerForPoint(gfx::Point(50, 50)));
525
526 View* parent_root_view_child = new View;
527 parent_root_view->AddChildView(parent_root_view_child);
528 parent_root_view_child->SetBounds(0, 0, 10, 10);
529
530 // Create a View whose layer extends outside the bounds of its parent. Event
531 // targetting should only consider the visible bounds.
532 View* parent_root_view_child_child = new View;
533 parent_root_view_child->AddChildView(parent_root_view_child_child);
534 parent_root_view_child_child->SetBounds(0, 0, 100, 100);
535 parent_root_view_child_child->SetPaintToLayer();
536 parent_root_view_child_child->layer()->parent()->StackAtTop(
537 parent_root_view_child_child->layer());
538
539 // 20,20 is over |parent_root_view_child_child|'s layer, but not the visible
540 // bounds of |parent_root_view_child_child|, so |child| should be the event
541 // target.
542 EXPECT_EQ(
543 child.GetNativeWindow(),
544 parent.GetNativeWindow()->GetEventHandlerForPoint(gfx::Point(20, 20)));
545 }
546
547 // Verifies that widget->FlashFrame() sets aura::client::kDrawAttentionKey,
548 // and activating the window clears it.
TEST_F(NativeWidgetAuraTest,FlashFrame)549 TEST_F(NativeWidgetAuraTest, FlashFrame) {
550 std::unique_ptr<Widget> widget(new Widget());
551 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
552 params.context = root_window();
553 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
554 widget->Init(std::move(params));
555 aura::Window* window = widget->GetNativeWindow();
556 EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey));
557 widget->FlashFrame(true);
558 EXPECT_TRUE(window->GetProperty(aura::client::kDrawAttentionKey));
559 widget->FlashFrame(false);
560 EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey));
561 widget->FlashFrame(true);
562 EXPECT_TRUE(window->GetProperty(aura::client::kDrawAttentionKey));
563 widget->Activate();
564 EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey));
565 }
566
TEST_F(NativeWidgetAuraTest,NoCrashOnThemeAfterClose)567 TEST_F(NativeWidgetAuraTest, NoCrashOnThemeAfterClose) {
568 std::unique_ptr<aura::Window> parent(new aura::Window(nullptr));
569 parent->Init(ui::LAYER_NOT_DRAWN);
570 parent->SetBounds(gfx::Rect(0, 0, 480, 320));
571 std::unique_ptr<Widget> widget(new Widget());
572 Init(parent.get(), widget.get());
573 widget->Show();
574 widget->Close();
575 base::RunLoop().RunUntilIdle();
576 widget->GetNativeTheme(); // Shouldn't crash.
577 }
578
579 // Used to track calls to WidgetDelegate::OnWidgetMove().
580 class MoveTestWidgetDelegate : public WidgetDelegateView {
581 public:
582 MoveTestWidgetDelegate() = default;
583 ~MoveTestWidgetDelegate() override = default;
584
ClearGotMove()585 void ClearGotMove() { got_move_ = false; }
got_move() const586 bool got_move() const { return got_move_; }
587
588 // WidgetDelegate overrides:
OnWidgetMove()589 void OnWidgetMove() override { got_move_ = true; }
590
591 private:
592 bool got_move_ = false;
593
594 DISALLOW_COPY_AND_ASSIGN(MoveTestWidgetDelegate);
595 };
596
597 // This test simulates what happens when a window is normally maximized. That
598 // is, it's layer is acquired for animation then the window is maximized.
599 // Acquiring the layer resets the bounds of the window. This test verifies the
600 // Widget is still notified correctly of a move in this case.
TEST_F(NativeWidgetAuraTest,OnWidgetMovedInvokedAfterAcquireLayer)601 TEST_F(NativeWidgetAuraTest, OnWidgetMovedInvokedAfterAcquireLayer) {
602 // |delegate| deletes itself when the widget is destroyed.
603 MoveTestWidgetDelegate* delegate = new MoveTestWidgetDelegate;
604 Widget* widget = Widget::CreateWindowWithContext(delegate, root_window(),
605 gfx::Rect(10, 10, 100, 200));
606 widget->Show();
607 delegate->ClearGotMove();
608 // Simulate a maximize with animation.
609 widget->GetNativeView()->RecreateLayer();
610 widget->SetBounds(gfx::Rect(0, 0, 500, 500));
611 EXPECT_TRUE(delegate->got_move());
612 widget->CloseNow();
613 }
614
615 // Tests that if a widget has a view which should be initially focused when the
616 // widget is shown, this view should not get focused if the associated window
617 // can not be activated.
TEST_F(NativeWidgetAuraTest,PreventFocusOnNonActivableWindow)618 TEST_F(NativeWidgetAuraTest, PreventFocusOnNonActivableWindow) {
619 test_focus_rules()->set_can_activate(false);
620 test::TestInitialFocusWidgetDelegate delegate(root_window());
621 delegate.GetWidget()->Show();
622 EXPECT_FALSE(delegate.view()->HasFocus());
623
624 test_focus_rules()->set_can_activate(true);
625 test::TestInitialFocusWidgetDelegate delegate2(root_window());
626 delegate2.GetWidget()->Show();
627 EXPECT_TRUE(delegate2.view()->HasFocus());
628 }
629
630 // Tests that the transient child bubble window is only visible if the parent is
631 // visible.
TEST_F(NativeWidgetAuraTest,VisibilityOfChildBubbleWindow)632 TEST_F(NativeWidgetAuraTest, VisibilityOfChildBubbleWindow) {
633 // Create a parent window.
634 Widget parent;
635 Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW);
636 parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
637 parent_params.context = root_window();
638 parent.Init(std::move(parent_params));
639 parent.SetBounds(gfx::Rect(0, 0, 480, 320));
640
641 // Add a child bubble window to the above parent window and show it.
642 Widget child;
643 Widget::InitParams child_params(Widget::InitParams::TYPE_BUBBLE);
644 child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
645 child_params.parent = parent.GetNativeWindow();
646 child.Init(std::move(child_params));
647 child.SetBounds(gfx::Rect(0, 0, 200, 200));
648 child.Show();
649
650 // Check that the bubble window is added as the transient child and it is
651 // hidden because parent window is hidden.
652 wm::TransientWindowManager* manager =
653 wm::TransientWindowManager::GetOrCreate(child.GetNativeWindow());
654 EXPECT_EQ(parent.GetNativeWindow(), manager->transient_parent());
655 EXPECT_FALSE(parent.IsVisible());
656 EXPECT_FALSE(child.IsVisible());
657
658 // Show the parent window should make the transient child bubble visible.
659 parent.Show();
660 EXPECT_TRUE(parent.IsVisible());
661 EXPECT_TRUE(child.IsVisible());
662 }
663
664 // Tests that for a child transient window, if its modal type is
665 // ui::MODAL_TYPE_WINDOW, then its visibility is controlled by its transient
666 // parent's visibility.
TEST_F(NativeWidgetAuraTest,TransientChildModalWindowVisibility)667 TEST_F(NativeWidgetAuraTest, TransientChildModalWindowVisibility) {
668 // Create a parent window.
669 Widget parent;
670 Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW);
671 parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
672 parent_params.context = root_window();
673 parent.Init(std::move(parent_params));
674 parent.SetBounds(gfx::Rect(0, 0, 400, 400));
675 parent.Show();
676 EXPECT_TRUE(parent.IsVisible());
677
678 // Create a ui::MODAL_TYPE_WINDOW modal type transient child window.
679 Widget child;
680 Widget::InitParams child_params(Widget::InitParams::TYPE_WINDOW);
681 child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
682 child_params.parent = parent.GetNativeWindow();
683 child_params.delegate = new WidgetDelegate;
684 child_params.delegate->SetOwnedByWidget(true);
685 child_params.delegate->SetModalType(ui::MODAL_TYPE_WINDOW);
686 child.Init(std::move(child_params));
687 child.SetBounds(gfx::Rect(0, 0, 200, 200));
688 child.Show();
689 EXPECT_TRUE(parent.IsVisible());
690 EXPECT_TRUE(child.IsVisible());
691
692 // Hide the parent window should also hide the child window.
693 parent.Hide();
694 EXPECT_FALSE(parent.IsVisible());
695 EXPECT_FALSE(child.IsVisible());
696
697 // The child window can't be shown if the parent window is hidden.
698 child.Show();
699 EXPECT_FALSE(parent.IsVisible());
700 EXPECT_FALSE(child.IsVisible());
701
702 parent.Show();
703 EXPECT_TRUE(parent.IsVisible());
704 EXPECT_TRUE(child.IsVisible());
705 }
706
707 } // namespace
708 } // namespace views
709