1 // Copyright 2013 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.
5 #include "ash/wm/immersive_fullscreen_controller.h"
7 #include "ash/display/display_manager.h"
8 #include "ash/display/mouse_cursor_event_filter.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shelf/shelf_layout_manager.h"
11 #include "ash/shelf/shelf_types.h"
12 #include "ash/shell.h"
13 #include "ash/test/ash_test_base.h"
14 #include "ash/wm/window_state.h"
15 #include "ui/aura/client/aura_constants.h"
16 #include "ui/aura/client/cursor_client.h"
17 #include "ui/aura/env.h"
18 #include "ui/aura/test/test_window_delegate.h"
19 #include "ui/aura/window.h"
20 #include "ui/aura/window_event_dispatcher.h"
21 #include "ui/events/event_utils.h"
22 #include "ui/events/test/event_generator.h"
23 #include "ui/events/test/test_event_handler.h"
24 #include "ui/gfx/animation/slide_animation.h"
25 #include "ui/views/bubble/bubble_delegate.h"
26 #include "ui/views/controls/native/native_view_host.h"
27 #include "ui/views/view.h"
28 #include "ui/views/widget/widget.h"
34 class MockImmersiveFullscreenControllerDelegate
35 : public ImmersiveFullscreenController::Delegate
{
37 MockImmersiveFullscreenControllerDelegate(views::View
* top_container_view
)
38 : top_container_view_(top_container_view
),
40 visible_fraction_(1) {
42 ~MockImmersiveFullscreenControllerDelegate() override
{}
44 // ImmersiveFullscreenController::Delegate overrides:
45 void OnImmersiveRevealStarted() override
{
47 visible_fraction_
= 0;
49 void OnImmersiveRevealEnded() override
{ visible_fraction_
= 0; }
50 void OnImmersiveFullscreenExited() override
{
52 visible_fraction_
= 1;
54 void SetVisibleFraction(double visible_fraction
) override
{
55 visible_fraction_
= visible_fraction
;
57 std::vector
<gfx::Rect
> GetVisibleBoundsInScreen() const override
{
58 std::vector
<gfx::Rect
> bounds_in_screen
;
59 bounds_in_screen
.push_back(top_container_view_
->GetBoundsInScreen());
60 return bounds_in_screen
;
63 bool is_enabled() const {
67 double visible_fraction() const {
68 return visible_fraction_
;
72 views::View
* top_container_view_
;
74 double visible_fraction_
;
76 DISALLOW_COPY_AND_ASSIGN(MockImmersiveFullscreenControllerDelegate
);
79 class ConsumeEventHandler
: public ui::test::TestEventHandler
{
81 ConsumeEventHandler() {}
82 ~ConsumeEventHandler() override
{}
85 void OnEvent(ui::Event
* event
) override
{
86 ui::test::TestEventHandler::OnEvent(event
);
87 if (event
->cancelable())
91 DISALLOW_COPY_AND_ASSIGN(ConsumeEventHandler
);
96 /////////////////////////////////////////////////////////////////////////////
98 class ImmersiveFullscreenControllerTest
: public ash::test::AshTestBase
{
102 MODALITY_GESTURE_TAP
,
103 MODALITY_GESTURE_SCROLL
106 ImmersiveFullscreenControllerTest()
108 top_container_(NULL
),
109 content_view_(NULL
) {}
110 ~ImmersiveFullscreenControllerTest() override
{}
112 ImmersiveFullscreenController
* controller() {
113 return controller_
.get();
116 views::NativeViewHost
* content_view() {
117 return content_view_
;
120 views::View
* top_container() {
121 return top_container_
;
124 views::Widget
* widget() { return widget_
; }
126 aura::Window
* window() {
127 return widget_
->GetNativeWindow();
130 MockImmersiveFullscreenControllerDelegate
* delegate() {
131 return delegate_
.get();
134 // Access to private data from the controller.
135 bool top_edge_hover_timer_running() const {
136 return controller_
->top_edge_hover_timer_
.IsRunning();
138 int mouse_x_when_hit_top() const {
139 return controller_
->mouse_x_when_hit_top_in_screen_
;
142 // ash::test::AshTestBase overrides:
143 void SetUp() override
{
144 ash::test::AshTestBase::SetUp();
146 widget_
= new views::Widget();
147 views::Widget::InitParams params
;
148 params
.context
= CurrentContext();
149 widget_
->Init(params
);
152 window()->SetProperty(aura::client::kShowStateKey
,
153 ui::SHOW_STATE_FULLSCREEN
);
155 gfx::Size window_size
= widget_
->GetWindowBoundsInScreen().size();
156 content_view_
= new views::NativeViewHost();
157 content_view_
->SetBounds(0, 0, window_size
.width(), window_size
.height());
158 widget_
->GetContentsView()->AddChildView(content_view_
);
160 top_container_
= new views::View();
161 top_container_
->SetBounds(
162 0, 0, window_size
.width(), 100);
163 top_container_
->SetFocusable(true);
164 widget_
->GetContentsView()->AddChildView(top_container_
);
167 new MockImmersiveFullscreenControllerDelegate(top_container_
));
168 controller_
.reset(new ImmersiveFullscreenController
);
169 controller_
->Init(delegate_
.get(), widget_
, top_container_
);
170 controller_
->SetupForTest();
172 // The mouse is moved so that it is not over |top_container_| by
176 // Enables / disables immersive fullscreen.
177 void SetEnabled(bool enabled
) {
178 controller_
->SetEnabled(ImmersiveFullscreenController::WINDOW_TYPE_OTHER
,
182 // Attempt to reveal the top-of-window views via |modality|.
183 // The top-of-window views can only be revealed via mouse hover or a gesture.
184 void AttemptReveal(Modality modality
) {
185 ASSERT_NE(modality
, MODALITY_GESTURE_TAP
);
186 AttemptRevealStateChange(true, modality
);
189 // Attempt to unreveal the top-of-window views via |modality|. The
190 // top-of-window views can be unrevealed via any modality.
191 void AttemptUnreveal(Modality modality
) {
192 AttemptRevealStateChange(false, modality
);
195 // Sets whether the mouse is hovered above |top_container_|.
196 // SetHovered(true) moves the mouse over the |top_container_| but does not
197 // move it to the top of the screen so will not initiate a reveal.
198 void SetHovered(bool is_mouse_hovered
) {
199 MoveMouse(0, is_mouse_hovered
? 10 : top_container_
->height() + 100);
202 // Move the mouse to the given coordinates. The coordinates should be in
203 // |top_container_| coordinates.
204 void MoveMouse(int x
, int y
) {
205 gfx::Point
screen_position(x
, y
);
206 views::View::ConvertPointToScreen(top_container_
, &screen_position
);
207 GetEventGenerator().MoveMouseTo(screen_position
.x(), screen_position
.y());
209 // If the top edge timer started running as a result of the mouse move, run
210 // the task which occurs after the timer delay. This reveals the
211 // top-of-window views synchronously if the mouse is hovered at the top of
213 if (controller()->top_edge_hover_timer_
.IsRunning()) {
214 controller()->top_edge_hover_timer_
.user_task().Run();
215 controller()->top_edge_hover_timer_
.Stop();
220 // Attempt to change the revealed state to |revealed| via |modality|.
221 void AttemptRevealStateChange(bool revealed
, Modality modality
) {
222 // Compute the event position in |top_container_| coordinates.
223 gfx::Point
event_position(0, revealed
? 0 : top_container_
->height() + 100);
225 case MODALITY_MOUSE
: {
226 MoveMouse(event_position
.x(), event_position
.y());
229 case MODALITY_GESTURE_TAP
: {
230 gfx::Point screen_position
= event_position
;
231 views::View::ConvertPointToScreen(top_container_
, &screen_position
);
232 ui::test::EventGenerator
& event_generator(GetEventGenerator());
233 event_generator
.MoveTouch(event_position
);
234 event_generator
.PressTouch();
235 event_generator
.ReleaseTouch();
238 case MODALITY_GESTURE_SCROLL
: {
239 gfx::Point
start(0, revealed
? 0 : top_container_
->height() - 2);
240 gfx::Vector2d
scroll_delta(0, 40);
241 gfx::Point end
= revealed
? start
+ scroll_delta
: start
- scroll_delta
;
242 views::View::ConvertPointToScreen(top_container_
, &start
);
243 views::View::ConvertPointToScreen(top_container_
, &end
);
244 ui::test::EventGenerator
& event_generator(GetEventGenerator());
245 event_generator
.GestureScrollSequence(
247 base::TimeDelta::FromMilliseconds(30), 1);
253 scoped_ptr
<ImmersiveFullscreenController
> controller_
;
254 scoped_ptr
<MockImmersiveFullscreenControllerDelegate
> delegate_
;
255 views::Widget
* widget_
; // Owned by the native widget.
256 views::View
* top_container_
; // Owned by |widget_|'s root-view.
257 views::NativeViewHost
* content_view_
; // Owned by |widget_|'s root-view.
259 DISALLOW_COPY_AND_ASSIGN(ImmersiveFullscreenControllerTest
);
262 // Test the initial state and that the delegate gets notified of the
263 // top-of-window views getting hidden and revealed.
264 TEST_F(ImmersiveFullscreenControllerTest
, Delegate
) {
266 EXPECT_FALSE(controller()->IsEnabled());
267 EXPECT_FALSE(controller()->IsRevealed());
268 EXPECT_FALSE(delegate()->is_enabled());
270 // Enabling initially hides the top views.
272 EXPECT_TRUE(controller()->IsEnabled());
273 EXPECT_FALSE(controller()->IsRevealed());
274 EXPECT_TRUE(delegate()->is_enabled());
275 EXPECT_EQ(0, delegate()->visible_fraction());
277 // Revealing shows the top views.
278 AttemptReveal(MODALITY_MOUSE
);
279 EXPECT_TRUE(controller()->IsEnabled());
280 EXPECT_TRUE(controller()->IsRevealed());
281 EXPECT_TRUE(delegate()->is_enabled());
282 EXPECT_EQ(1, delegate()->visible_fraction());
284 // Disabling ends the immersive reveal.
286 EXPECT_FALSE(controller()->IsEnabled());
287 EXPECT_FALSE(controller()->IsRevealed());
288 EXPECT_FALSE(delegate()->is_enabled());
291 // GetRevealedLock() specific tests.
292 TEST_F(ImmersiveFullscreenControllerTest
, RevealedLock
) {
293 scoped_ptr
<ImmersiveRevealedLock
> lock1
;
294 scoped_ptr
<ImmersiveRevealedLock
> lock2
;
296 // Immersive fullscreen is not on by default.
297 EXPECT_FALSE(controller()->IsEnabled());
299 // 1) Test acquiring and releasing a revealed state lock while immersive
300 // fullscreen is disabled. Acquiring or releasing the lock should have no
301 // effect till immersive fullscreen is enabled.
302 lock1
.reset(controller()->GetRevealedLock(
303 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
304 EXPECT_FALSE(controller()->IsEnabled());
305 EXPECT_FALSE(controller()->IsRevealed());
307 // Immersive fullscreen should start in the revealed state due to the lock.
309 EXPECT_TRUE(controller()->IsEnabled());
310 EXPECT_TRUE(controller()->IsRevealed());
313 EXPECT_FALSE(controller()->IsEnabled());
314 EXPECT_FALSE(controller()->IsRevealed());
317 EXPECT_FALSE(controller()->IsEnabled());
318 EXPECT_FALSE(controller()->IsRevealed());
320 // Immersive fullscreen should start in the closed state because the lock is
323 EXPECT_TRUE(controller()->IsEnabled());
324 EXPECT_FALSE(controller()->IsRevealed());
326 // 2) Test that acquiring a lock reveals the top-of-window views if they are
328 lock1
.reset(controller()->GetRevealedLock(
329 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
330 EXPECT_TRUE(controller()->IsRevealed());
332 // 3) Test that the top-of-window views are only hidden when all of the locks
334 lock2
.reset(controller()->GetRevealedLock(
335 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
337 EXPECT_TRUE(controller()->IsRevealed());
340 EXPECT_FALSE(controller()->IsRevealed());
343 // Test mouse event processing for top-of-screen reveal triggering.
344 TEST_F(ImmersiveFullscreenControllerTest
, OnMouseEvent
) {
345 // Set up initial state.
347 ASSERT_TRUE(controller()->IsEnabled());
348 ASSERT_FALSE(controller()->IsRevealed());
350 ui::test::EventGenerator
& event_generator(GetEventGenerator());
352 gfx::Rect top_container_bounds_in_screen
=
353 top_container()->GetBoundsInScreen();
354 // A position along the top edge of TopContainerView in screen coordinates.
355 gfx::Point
top_edge_pos(top_container_bounds_in_screen
.x() + 100,
356 top_container_bounds_in_screen
.y());
358 // Mouse wheel event does nothing.
359 ui::MouseEvent
wheel(ui::ET_MOUSEWHEEL
, top_edge_pos
, top_edge_pos
,
360 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
361 event_generator
.Dispatch(&wheel
);
362 EXPECT_FALSE(top_edge_hover_timer_running());
364 // Move to top edge of screen starts hover timer running. We cannot use
365 // MoveMouse() because MoveMouse() stops the timer if it started running.
366 event_generator
.MoveMouseTo(top_edge_pos
);
367 EXPECT_TRUE(top_edge_hover_timer_running());
368 EXPECT_EQ(top_edge_pos
.x(), mouse_x_when_hit_top());
370 // Moving |ImmersiveFullscreenControllerTest::kMouseRevealBoundsHeight| down
371 // from the top edge stops it.
372 event_generator
.MoveMouseBy(0,
373 ImmersiveFullscreenController::kMouseRevealBoundsHeight
);
374 EXPECT_FALSE(top_edge_hover_timer_running());
376 // Moving back to the top starts the timer again.
377 event_generator
.MoveMouseTo(top_edge_pos
);
378 EXPECT_TRUE(top_edge_hover_timer_running());
379 EXPECT_EQ(top_edge_pos
.x(), mouse_x_when_hit_top());
381 // Slight move to the right keeps the timer running for the same hit point.
382 event_generator
.MoveMouseBy(1, 0);
383 EXPECT_TRUE(top_edge_hover_timer_running());
384 EXPECT_EQ(top_edge_pos
.x(), mouse_x_when_hit_top());
386 // Moving back to the left also keeps the timer running.
387 event_generator
.MoveMouseBy(-1, 0);
388 EXPECT_TRUE(top_edge_hover_timer_running());
389 EXPECT_EQ(top_edge_pos
.x(), mouse_x_when_hit_top());
391 // Large move right restarts the timer (so it is still running) and considers
392 // this a new hit at the top.
393 event_generator
.MoveMouseTo(top_edge_pos
.x() + 100, top_edge_pos
.y());
394 EXPECT_TRUE(top_edge_hover_timer_running());
395 EXPECT_EQ(top_edge_pos
.x() + 100, mouse_x_when_hit_top());
397 // Moving off the top edge horizontally stops the timer.
398 event_generator
.MoveMouseTo(top_container_bounds_in_screen
.right() + 1,
399 top_container_bounds_in_screen
.y());
400 EXPECT_FALSE(top_edge_hover_timer_running());
402 // Once revealed, a move just a little below the top container doesn't end a
404 AttemptReveal(MODALITY_MOUSE
);
405 event_generator
.MoveMouseTo(top_container_bounds_in_screen
.x(),
406 top_container_bounds_in_screen
.bottom() + 1);
407 EXPECT_TRUE(controller()->IsRevealed());
409 // Once revealed, clicking just below the top container ends the reveal.
410 event_generator
.ClickLeftButton();
411 EXPECT_FALSE(controller()->IsRevealed());
413 // Moving a lot below the top container ends a reveal.
414 AttemptReveal(MODALITY_MOUSE
);
415 EXPECT_TRUE(controller()->IsRevealed());
416 event_generator
.MoveMouseTo(top_container_bounds_in_screen
.x(),
417 top_container_bounds_in_screen
.bottom() + 50);
418 EXPECT_FALSE(controller()->IsRevealed());
420 // The mouse position cannot cause a reveal when the top container's widget
422 views::Widget
* widget
= top_container()->GetWidget();
423 widget
->SetCapture(top_container());
424 AttemptReveal(MODALITY_MOUSE
);
425 EXPECT_FALSE(controller()->IsRevealed());
426 widget
->ReleaseCapture();
428 // The mouse position cannot end the reveal while the top container's widget
430 AttemptReveal(MODALITY_MOUSE
);
431 EXPECT_TRUE(controller()->IsRevealed());
432 widget
->SetCapture(top_container());
433 event_generator
.MoveMouseTo(top_container_bounds_in_screen
.x(),
434 top_container_bounds_in_screen
.bottom() + 51);
435 EXPECT_TRUE(controller()->IsRevealed());
437 // Releasing capture should end the reveal.
438 widget
->ReleaseCapture();
439 EXPECT_FALSE(controller()->IsRevealed());
442 // Test mouse event processing for top-of-screen reveal triggering when the
443 // top container's widget is inactive.
444 TEST_F(ImmersiveFullscreenControllerTest
, Inactive
) {
445 // Set up initial state.
446 views::Widget
* popup_widget
= views::Widget::CreateWindowWithContextAndBounds(
449 gfx::Rect(0, 0, 200, 200));
450 popup_widget
->Show();
451 ASSERT_FALSE(top_container()->GetWidget()->IsActive());
454 ASSERT_TRUE(controller()->IsEnabled());
455 ASSERT_FALSE(controller()->IsRevealed());
457 gfx::Rect top_container_bounds_in_screen
=
458 top_container()->GetBoundsInScreen();
459 gfx::Rect popup_bounds_in_screen
= popup_widget
->GetWindowBoundsInScreen();
460 ASSERT_EQ(top_container_bounds_in_screen
.origin().ToString(),
461 popup_bounds_in_screen
.origin().ToString());
462 ASSERT_GT(top_container_bounds_in_screen
.right(),
463 popup_bounds_in_screen
.right());
465 // The top-of-window views should stay hidden if the cursor is at the top edge
466 // but above an obscured portion of the top-of-window views.
467 MoveMouse(popup_bounds_in_screen
.x(),
468 top_container_bounds_in_screen
.y());
469 EXPECT_FALSE(controller()->IsRevealed());
471 // The top-of-window views should reveal if the cursor is at the top edge and
472 // above an unobscured portion of the top-of-window views.
473 MoveMouse(top_container_bounds_in_screen
.right() - 1,
474 top_container_bounds_in_screen
.y());
475 EXPECT_TRUE(controller()->IsRevealed());
477 // The top-of-window views should stay revealed if the cursor is moved off
479 MoveMouse(top_container_bounds_in_screen
.right() - 1,
480 top_container_bounds_in_screen
.bottom() - 1);
481 EXPECT_TRUE(controller()->IsRevealed());
483 // Moving way off of the top-of-window views should end the immersive reveal.
484 MoveMouse(top_container_bounds_in_screen
.right() - 1,
485 top_container_bounds_in_screen
.bottom() + 50);
486 EXPECT_FALSE(controller()->IsRevealed());
488 // Moving way off of the top-of-window views in a region where the
489 // top-of-window views are obscured should also end the immersive reveal.
490 // Ideally, the immersive reveal would end immediately when the cursor moves
491 // to an obscured portion of the top-of-window views.
492 MoveMouse(top_container_bounds_in_screen
.right() - 1,
493 top_container_bounds_in_screen
.y());
494 EXPECT_TRUE(controller()->IsRevealed());
495 MoveMouse(top_container_bounds_in_screen
.x(),
496 top_container_bounds_in_screen
.bottom() + 50);
497 EXPECT_FALSE(controller()->IsRevealed());
500 // Test mouse event processing for top-of-screen reveal triggering when the user
501 // has a vertical display layout (primary display above/below secondary display)
502 // and the immersive fullscreen window is on the bottom display.
503 TEST_F(ImmersiveFullscreenControllerTest
, MouseEventsVerticalDisplayLayout
) {
504 if (!SupportsMultipleDisplays())
507 // Set up initial state.
508 UpdateDisplay("800x600,800x600");
509 ash::DisplayLayout
display_layout(ash::DisplayLayout::TOP
, 0);
510 ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays(
514 ASSERT_TRUE(controller()->IsEnabled());
515 ASSERT_FALSE(controller()->IsRevealed());
517 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
518 ASSERT_EQ(root_windows
[0],
519 top_container()->GetWidget()->GetNativeWindow()->GetRootWindow());
521 gfx::Rect primary_root_window_bounds_in_screen
=
522 root_windows
[0]->GetBoundsInScreen();
523 // Do not set |x| to the root window's x position because the display's
524 // corners have special behavior.
525 int x
= primary_root_window_bounds_in_screen
.x() + 10;
526 // The y position of the top edge of the primary display.
527 int y_top_edge
= primary_root_window_bounds_in_screen
.y();
529 ui::test::EventGenerator
& event_generator(GetEventGenerator());
531 // Moving right below the top edge starts the hover timer running. We
532 // cannot use MoveMouse() because MoveMouse() stops the timer if it started
534 event_generator
.MoveMouseTo(x
, y_top_edge
+ 1);
535 EXPECT_TRUE(top_edge_hover_timer_running());
536 EXPECT_EQ(y_top_edge
+ 1,
537 aura::Env::GetInstance()->last_mouse_location().y());
539 // The timer should continue running if the user moves the mouse to the top
540 // edge even though the mouse is warped to the secondary display.
541 event_generator
.MoveMouseTo(x
, y_top_edge
);
542 EXPECT_TRUE(top_edge_hover_timer_running());
544 // The timer should continue running if the user overshoots the top edge
546 event_generator
.MoveMouseTo(x
, y_top_edge
- 2);
547 EXPECT_TRUE(top_edge_hover_timer_running());
549 // The timer should stop running if the user overshoots the top edge by
551 event_generator
.MoveMouseTo(x
, y_top_edge
- 20);
552 EXPECT_FALSE(top_edge_hover_timer_running());
554 // The timer should not start if the user moves the mouse to the bottom of the
555 // secondary display without crossing the top edge first.
556 event_generator
.MoveMouseTo(x
, y_top_edge
- 2);
558 // Reveal the top-of-window views by overshooting the top edge slightly.
559 event_generator
.MoveMouseTo(x
, y_top_edge
+ 1);
560 // MoveMouse() runs the timer task.
561 MoveMouse(x
, y_top_edge
- 2);
562 EXPECT_TRUE(controller()->IsRevealed());
564 // The top-of-window views should stay revealed if the user moves the mouse
565 // around in the bottom region of the secondary display.
566 event_generator
.MoveMouseTo(x
+ 10, y_top_edge
- 3);
567 EXPECT_TRUE(controller()->IsRevealed());
569 // The top-of-window views should hide if the user moves the mouse away from
570 // the bottom region of the secondary display.
571 event_generator
.MoveMouseTo(x
, y_top_edge
- 20);
572 EXPECT_FALSE(controller()->IsRevealed());
574 // Test that it is possible to reveal the top-of-window views by overshooting
575 // the top edge slightly when the top container's widget is not active.
576 views::Widget
* popup_widget
= views::Widget::CreateWindowWithContextAndBounds(
579 gfx::Rect(0, 200, 100, 100));
580 popup_widget
->Show();
581 ASSERT_FALSE(top_container()->GetWidget()->IsActive());
582 ASSERT_FALSE(top_container()->GetBoundsInScreen().Intersects(
583 popup_widget
->GetWindowBoundsInScreen()));
584 event_generator
.MoveMouseTo(x
, y_top_edge
+ 1);
585 MoveMouse(x
, y_top_edge
- 2);
586 EXPECT_TRUE(controller()->IsRevealed());
589 // Test behavior when the mouse becomes hovered without moving.
590 TEST_F(ImmersiveFullscreenControllerTest
, MouseHoveredWithoutMoving
) {
592 scoped_ptr
<ImmersiveRevealedLock
> lock
;
594 // 1) Test that if the mouse becomes hovered without the mouse moving due to a
595 // lock causing the top-of-window views to be revealed (and the mouse
596 // happening to be near the top of the screen), the top-of-window views do not
597 // hide till the mouse moves off of the top-of-window views.
599 EXPECT_FALSE(controller()->IsRevealed());
600 lock
.reset(controller()->GetRevealedLock(
601 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
602 EXPECT_TRUE(controller()->IsRevealed());
604 EXPECT_TRUE(controller()->IsRevealed());
606 EXPECT_FALSE(controller()->IsRevealed());
608 // 2) Test that if the mouse becomes hovered without moving because of a
609 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there are no
610 // locks keeping the top-of-window views revealed, that mouse hover does not
611 // prevent the top-of-window views from closing.
614 EXPECT_FALSE(controller()->IsRevealed());
616 EXPECT_FALSE(controller()->IsRevealed());
618 // 3) Test that if the mouse becomes hovered without moving because of a
619 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there is a
620 // lock keeping the top-of-window views revealed, that the top-of-window views
621 // do not hide till the mouse moves off of the top-of-window views.
624 lock
.reset(controller()->GetRevealedLock(
625 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
626 EXPECT_FALSE(controller()->IsRevealed());
628 EXPECT_TRUE(controller()->IsRevealed());
630 EXPECT_TRUE(controller()->IsRevealed());
632 EXPECT_FALSE(controller()->IsRevealed());
635 // Test revealing the top-of-window views using one modality and ending
636 // the reveal via another. For instance, initiating the reveal via a SWIPE_OPEN
637 // edge gesture, switching to using the mouse and ending the reveal by moving
638 // the mouse off of the top-of-window views.
639 TEST_F(ImmersiveFullscreenControllerTest
, DifferentModalityEnterExit
) {
641 EXPECT_TRUE(controller()->IsEnabled());
642 EXPECT_FALSE(controller()->IsRevealed());
644 // Initiate reveal via gesture, end reveal via mouse.
645 AttemptReveal(MODALITY_GESTURE_SCROLL
);
646 EXPECT_TRUE(controller()->IsRevealed());
648 EXPECT_TRUE(controller()->IsRevealed());
649 AttemptUnreveal(MODALITY_MOUSE
);
650 EXPECT_FALSE(controller()->IsRevealed());
652 // Initiate reveal via gesture, end reveal via touch.
653 AttemptReveal(MODALITY_GESTURE_SCROLL
);
654 EXPECT_TRUE(controller()->IsRevealed());
655 AttemptUnreveal(MODALITY_GESTURE_TAP
);
656 EXPECT_FALSE(controller()->IsRevealed());
658 // Initiate reveal via mouse, end reveal via gesture.
659 AttemptReveal(MODALITY_MOUSE
);
660 EXPECT_TRUE(controller()->IsRevealed());
661 AttemptUnreveal(MODALITY_GESTURE_SCROLL
);
662 EXPECT_FALSE(controller()->IsRevealed());
664 // Initiate reveal via mouse, end reveal via touch.
665 AttemptReveal(MODALITY_MOUSE
);
666 EXPECT_TRUE(controller()->IsRevealed());
667 AttemptUnreveal(MODALITY_GESTURE_TAP
);
668 EXPECT_FALSE(controller()->IsRevealed());
671 // Test when the SWIPE_CLOSE edge gesture closes the top-of-window views.
673 // On Windows, touch events do not result in mouse events being disabled. As
674 // a result, the last part of this test which ends the reveal via a gesture will
675 // not work correctly. See crbug.com/332430, and the function
676 // ShouldHideCursorOnTouch() in compound_event_filter.cc.
677 #define MAYBE_EndRevealViaGesture DISABLED_EndRevealViaGesture
679 #define MAYBE_EndRevealViaGesture EndRevealViaGesture
681 TEST_F(ImmersiveFullscreenControllerTest
, MAYBE_EndRevealViaGesture
) {
683 EXPECT_TRUE(controller()->IsEnabled());
684 EXPECT_FALSE(controller()->IsRevealed());
686 // A gesture should be able to close the top-of-window views when
687 // top-of-window views have focus.
688 AttemptReveal(MODALITY_MOUSE
);
689 top_container()->RequestFocus();
690 EXPECT_TRUE(controller()->IsRevealed());
691 AttemptUnreveal(MODALITY_GESTURE_SCROLL
);
692 EXPECT_FALSE(controller()->IsRevealed());
694 // The top-of-window views should no longer have focus. Clearing focus is
695 // important because it closes focus-related popup windows like the touch
696 // selection handles.
697 EXPECT_FALSE(top_container()->HasFocus());
699 // If some other code is holding onto a lock, a gesture should not be able to
701 AttemptReveal(MODALITY_MOUSE
);
702 scoped_ptr
<ImmersiveRevealedLock
> lock(controller()->GetRevealedLock(
703 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
704 EXPECT_TRUE(controller()->IsRevealed());
705 AttemptUnreveal(MODALITY_GESTURE_SCROLL
);
706 EXPECT_TRUE(controller()->IsRevealed());
708 EXPECT_FALSE(controller()->IsRevealed());
711 // Tests that touch-gesture can be used to reveal the top-of-window views when
712 // the child window consumes all events.
713 TEST_F(ImmersiveFullscreenControllerTest
, RevealViaGestureChildConsumesEvents
) {
714 // Enabling initially hides the top views.
716 EXPECT_TRUE(controller()->IsEnabled());
717 EXPECT_FALSE(controller()->IsRevealed());
719 aura::test::TestWindowDelegate child_delegate
;
720 scoped_ptr
<aura::Window
> child(
721 CreateTestWindowInShellWithDelegateAndType(&child_delegate
,
722 ui::wm::WINDOW_TYPE_CONTROL
,
725 content_view()->Attach(child
.get());
728 ConsumeEventHandler handler
;
729 child
->AddPreTargetHandler(&handler
);
731 // Reveal the top views using a touch-scroll gesture. The child window should
732 // not receive the touch events.
733 AttemptReveal(MODALITY_GESTURE_SCROLL
);
734 EXPECT_TRUE(controller()->IsRevealed());
735 EXPECT_EQ(0, handler
.num_touch_events());
737 AttemptUnreveal(MODALITY_GESTURE_TAP
);
738 EXPECT_FALSE(controller()->IsRevealed());
739 EXPECT_GT(handler
.num_touch_events(), 0);
740 child
->RemovePreTargetHandler(&handler
);
743 // Make sure touch events towards the top of the window do not leak through to
744 // windows underneath.
745 TEST_F(ImmersiveFullscreenControllerTest
, EventsDoNotLeakToWindowUnderneath
) {
746 gfx::Rect window_bounds
= window()->GetBoundsInScreen();
747 aura::test::TestWindowDelegate child_delegate
;
748 scoped_ptr
<aura::Window
> behind(CreateTestWindowInShellWithDelegate(
749 &child_delegate
, 1234, window_bounds
));
751 behind
->SetBounds(window_bounds
);
752 widget()->StackAbove(behind
.get());
754 // Make sure the windows are aligned on top.
755 EXPECT_EQ(behind
->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
756 int top
= behind
->GetBoundsInScreen().y();
758 ui::TouchEvent
touch(ui::ET_TOUCH_MOVED
, gfx::Point(10, top
), 0,
759 ui::EventTimeForNow());
760 ui::EventTarget
* root
= window()->GetRootWindow();
761 ui::EventTargeter
* targeter
= root
->GetEventTargeter();
762 EXPECT_EQ(window(), targeter
->FindTargetForEvent(root
, &touch
));
765 EXPECT_FALSE(controller()->IsRevealed());
766 // Make sure the windows are still aligned on top.
767 EXPECT_EQ(behind
->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
768 top
= behind
->GetBoundsInScreen().y();
769 ui::TouchEvent
touch2(ui::ET_TOUCH_MOVED
, gfx::Point(10, top
), 0,
770 ui::EventTimeForNow());
771 // The event should still be targeted to window().
772 EXPECT_EQ(window(), targeter
->FindTargetForEvent(root
, &touch2
));
775 // Check that the window state gets properly marked for immersive fullscreen.
776 TEST_F(ImmersiveFullscreenControllerTest
, WindowStateImmersiveFullscreen
) {
777 ash::wm::WindowState
* window_state
= ash::wm::GetWindowState(window());
779 EXPECT_FALSE(window_state
->in_immersive_fullscreen());
781 ASSERT_TRUE(controller()->IsEnabled());
782 EXPECT_TRUE(window_state
->in_immersive_fullscreen());
785 ASSERT_FALSE(controller()->IsEnabled());
786 EXPECT_FALSE(window_state
->in_immersive_fullscreen());
789 // Do not test under windows because focus testing is not reliable on
790 // Windows. (crbug.com/79493)
793 // Test how focus and activation affects whether the top-of-window views are
795 TEST_F(ImmersiveFullscreenControllerTest
, Focus
) {
796 // Add views to the view hierarchy which we will focus and unfocus during the
798 views::View
* child_view
= new views::View();
799 child_view
->SetBounds(0, 0, 10, 10);
800 child_view
->SetFocusable(true);
801 top_container()->AddChildView(child_view
);
802 views::View
* unrelated_view
= new views::View();
803 unrelated_view
->SetBounds(0, 100, 10, 10);
804 unrelated_view
->SetFocusable(true);
805 top_container()->parent()->AddChildView(unrelated_view
);
806 views::FocusManager
* focus_manager
=
807 top_container()->GetWidget()->GetFocusManager();
811 // 1) Test that the top-of-window views stay revealed as long as either a
812 // |child_view| has focus or the mouse is hovered above the top-of-window
814 AttemptReveal(MODALITY_MOUSE
);
815 child_view
->RequestFocus();
816 focus_manager
->ClearFocus();
817 EXPECT_TRUE(controller()->IsRevealed());
818 child_view
->RequestFocus();
820 EXPECT_TRUE(controller()->IsRevealed());
821 focus_manager
->ClearFocus();
822 EXPECT_FALSE(controller()->IsRevealed());
824 // 2) Test that focusing |unrelated_view| hides the top-of-window views.
825 // Note: In this test we can cheat and trigger a reveal via focus because
826 // the top container does not hide when the top-of-window views are not
828 child_view
->RequestFocus();
829 EXPECT_TRUE(controller()->IsRevealed());
830 unrelated_view
->RequestFocus();
831 EXPECT_FALSE(controller()->IsRevealed());
833 // 3) Test that a loss of focus of |child_view| to |unrelated_view|
834 // while immersive mode is disabled is properly registered.
835 child_view
->RequestFocus();
836 EXPECT_TRUE(controller()->IsRevealed());
838 EXPECT_FALSE(controller()->IsRevealed());
839 unrelated_view
->RequestFocus();
841 EXPECT_FALSE(controller()->IsRevealed());
843 // Repeat test but with a revealed lock acquired when immersive mode is
844 // disabled because the code path is different.
845 child_view
->RequestFocus();
846 EXPECT_TRUE(controller()->IsRevealed());
848 scoped_ptr
<ImmersiveRevealedLock
> lock(controller()->GetRevealedLock(
849 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
850 EXPECT_FALSE(controller()->IsRevealed());
851 unrelated_view
->RequestFocus();
853 EXPECT_TRUE(controller()->IsRevealed());
855 EXPECT_FALSE(controller()->IsRevealed());
858 // Test how transient windows affect whether the top-of-window views are
860 TEST_F(ImmersiveFullscreenControllerTest
, Transient
) {
861 views::Widget
* top_container_widget
= top_container()->GetWidget();
864 ASSERT_FALSE(controller()->IsRevealed());
866 // 1) Test that a transient window which is not a bubble does not trigger a
867 // reveal but does keep the top-of-window views revealed if they are already
869 views::Widget::InitParams transient_params
;
870 transient_params
.ownership
=
871 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
872 transient_params
.parent
= top_container_widget
->GetNativeView();
873 transient_params
.bounds
= gfx::Rect(0, 100, 100, 100);
874 scoped_ptr
<views::Widget
> transient_widget(new views::Widget());
875 transient_widget
->Init(transient_params
);
877 EXPECT_FALSE(controller()->IsRevealed());
878 AttemptReveal(MODALITY_MOUSE
);
879 EXPECT_TRUE(controller()->IsRevealed());
880 transient_widget
->Show();
882 EXPECT_TRUE(controller()->IsRevealed());
883 transient_widget
.reset();
884 EXPECT_FALSE(controller()->IsRevealed());
886 // 2) Test that activating a non-transient window does not keep the
887 // top-of-window views revealed.
888 views::Widget::InitParams non_transient_params
;
889 non_transient_params
.ownership
=
890 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
891 non_transient_params
.context
= top_container_widget
->GetNativeView();
892 non_transient_params
.bounds
= gfx::Rect(0, 100, 100, 100);
893 scoped_ptr
<views::Widget
> non_transient_widget(new views::Widget());
894 non_transient_widget
->Init(non_transient_params
);
896 EXPECT_FALSE(controller()->IsRevealed());
897 AttemptReveal(MODALITY_MOUSE
);
898 EXPECT_TRUE(controller()->IsRevealed());
899 non_transient_widget
->Show();
901 EXPECT_FALSE(controller()->IsRevealed());
904 // Test how bubbles affect whether the top-of-window views are revealed.
905 TEST_F(ImmersiveFullscreenControllerTest
, Bubbles
) {
906 scoped_ptr
<ImmersiveRevealedLock
> revealed_lock
;
907 views::Widget
* top_container_widget
= top_container()->GetWidget();
909 // Add views to the view hierarchy to which we will anchor bubbles.
910 views::View
* child_view
= new views::View();
911 child_view
->SetBounds(0, 0, 10, 10);
912 top_container()->AddChildView(child_view
);
913 views::View
* unrelated_view
= new views::View();
914 unrelated_view
->SetBounds(0, 100, 10, 10);
915 top_container()->parent()->AddChildView(unrelated_view
);
918 ASSERT_FALSE(controller()->IsRevealed());
920 // 1) Test that a bubble anchored to a child of the top container triggers
921 // a reveal and keeps the top-of-window views revealed for the duration of
923 views::Widget
* bubble_widget1(views::BubbleDelegateView::CreateBubble(
924 new views::BubbleDelegateView(child_view
, views::BubbleBorder::NONE
)));
925 bubble_widget1
->Show();
926 EXPECT_TRUE(controller()->IsRevealed());
928 // Activating |top_container_widget| will close |bubble_widget1|.
929 top_container_widget
->Activate();
930 AttemptReveal(MODALITY_MOUSE
);
931 revealed_lock
.reset(controller()->GetRevealedLock(
932 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
933 EXPECT_TRUE(controller()->IsRevealed());
935 views::Widget
* bubble_widget2
= views::BubbleDelegateView::CreateBubble(
936 new views::BubbleDelegateView(child_view
, views::BubbleBorder::NONE
));
937 bubble_widget2
->Show();
938 EXPECT_TRUE(controller()->IsRevealed());
939 revealed_lock
.reset();
941 EXPECT_TRUE(controller()->IsRevealed());
942 bubble_widget2
->Close();
943 EXPECT_FALSE(controller()->IsRevealed());
945 // 2) Test that transitioning from keeping the top-of-window views revealed
946 // because of a bubble to keeping the top-of-window views revealed because of
947 // mouse hover by activating |top_container_widget| works.
948 views::Widget
* bubble_widget3
= views::BubbleDelegateView::CreateBubble(
949 new views::BubbleDelegateView(child_view
, views::BubbleBorder::NONE
));
950 bubble_widget3
->Show();
952 EXPECT_TRUE(controller()->IsRevealed());
953 top_container_widget
->Activate();
954 EXPECT_TRUE(controller()->IsRevealed());
956 // 3) Test that the top-of-window views stay revealed as long as at least one
957 // bubble anchored to a child of the top container is visible.
959 EXPECT_FALSE(controller()->IsRevealed());
961 views::BubbleDelegateView
* bubble_delegate4(new views::BubbleDelegateView(
962 child_view
, views::BubbleBorder::NONE
));
963 bubble_delegate4
->set_can_activate(false);
964 views::Widget
* bubble_widget4(views::BubbleDelegateView::CreateBubble(
966 bubble_widget4
->Show();
968 views::BubbleDelegateView
* bubble_delegate5(new views::BubbleDelegateView(
969 child_view
, views::BubbleBorder::NONE
));
970 bubble_delegate5
->set_can_activate(false);
971 views::Widget
* bubble_widget5(views::BubbleDelegateView::CreateBubble(
973 bubble_widget5
->Show();
975 EXPECT_TRUE(controller()->IsRevealed());
976 bubble_widget4
->Hide();
977 EXPECT_TRUE(controller()->IsRevealed());
978 bubble_widget5
->Hide();
979 EXPECT_FALSE(controller()->IsRevealed());
980 bubble_widget5
->Show();
981 EXPECT_TRUE(controller()->IsRevealed());
983 // 4) Test that visibility changes which occur while immersive fullscreen is
984 // disabled are handled upon reenabling immersive fullscreen.
986 bubble_widget5
->Hide();
988 EXPECT_FALSE(controller()->IsRevealed());
990 // We do not need |bubble_widget4| or |bubble_widget5| anymore, close them.
991 bubble_widget4
->Close();
992 bubble_widget5
->Close();
994 // 5) Test that a bubble added while immersive fullscreen is disabled is
995 // handled upon reenabling immersive fullscreen.
998 views::Widget
* bubble_widget6
= views::BubbleDelegateView::CreateBubble(
999 new views::BubbleDelegateView(child_view
, views::BubbleBorder::NONE
));
1000 bubble_widget6
->Show();
1003 EXPECT_TRUE(controller()->IsRevealed());
1005 bubble_widget6
->Close();
1007 // 6) Test that a bubble which is not anchored to a child of the
1008 // TopContainerView does not trigger a reveal or keep the
1009 // top-of-window views revealed if they are already revealed.
1010 views::Widget
* bubble_widget7
= views::BubbleDelegateView::CreateBubble(
1011 new views::BubbleDelegateView(unrelated_view
, views::BubbleBorder::NONE
));
1012 bubble_widget7
->Show();
1013 EXPECT_FALSE(controller()->IsRevealed());
1015 // Activating |top_container_widget| will close |bubble_widget6|.
1016 top_container_widget
->Activate();
1017 AttemptReveal(MODALITY_MOUSE
);
1018 EXPECT_TRUE(controller()->IsRevealed());
1020 views::Widget
* bubble_widget8
= views::BubbleDelegateView::CreateBubble(
1021 new views::BubbleDelegateView(unrelated_view
, views::BubbleBorder::NONE
));
1022 bubble_widget8
->Show();
1024 EXPECT_FALSE(controller()->IsRevealed());
1025 bubble_widget8
->Close();
1028 #endif // defined(OS_WIN)
1030 // Test that the shelf is set to auto hide as long as the window is in
1031 // immersive fullscreen and that the shelf's state before entering immersive
1032 // fullscreen is restored upon exiting immersive fullscreen.
1033 TEST_F(ImmersiveFullscreenControllerTest
, Shelf
) {
1034 ash::ShelfLayoutManager
* shelf
=
1035 ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
1037 // Shelf is visible by default.
1038 window()->SetProperty(aura::client::kShowStateKey
, ui::SHOW_STATE_NORMAL
);
1039 ASSERT_FALSE(controller()->IsEnabled());
1040 ASSERT_EQ(ash::SHELF_VISIBLE
, shelf
->visibility_state());
1042 // Entering immersive fullscreen sets the shelf to auto hide.
1043 window()->SetProperty(aura::client::kShowStateKey
, ui::SHOW_STATE_FULLSCREEN
);
1045 EXPECT_EQ(ash::SHELF_AUTO_HIDE
, shelf
->visibility_state());
1047 // Disabling immersive fullscreen puts it back.
1049 window()->SetProperty(aura::client::kShowStateKey
, ui::SHOW_STATE_NORMAL
);
1050 ASSERT_FALSE(controller()->IsEnabled());
1051 EXPECT_EQ(ash::SHELF_VISIBLE
, shelf
->visibility_state());
1053 // The user could toggle the shelf auto-hide behavior.
1054 shelf
->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
);
1055 EXPECT_EQ(ash::SHELF_AUTO_HIDE
, shelf
->visibility_state());
1057 // Entering immersive fullscreen keeps auto-hide.
1058 window()->SetProperty(aura::client::kShowStateKey
, ui::SHOW_STATE_FULLSCREEN
);
1060 EXPECT_EQ(ash::SHELF_AUTO_HIDE
, shelf
->visibility_state());
1062 // Disabling immersive fullscreen maintains the user's auto-hide selection.
1064 window()->SetProperty(aura::client::kShowStateKey
,
1065 ui::SHOW_STATE_NORMAL
);
1066 EXPECT_EQ(ash::SHELF_AUTO_HIDE
, shelf
->visibility_state());