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 virtual ~MockImmersiveFullscreenControllerDelegate() {}
44 // ImmersiveFullscreenController::Delegate overrides:
45 virtual void OnImmersiveRevealStarted() OVERRIDE
{
47 visible_fraction_
= 0;
49 virtual void OnImmersiveRevealEnded() OVERRIDE
{
50 visible_fraction_
= 0;
52 virtual void OnImmersiveFullscreenExited() OVERRIDE
{
54 visible_fraction_
= 1;
56 virtual void SetVisibleFraction(double visible_fraction
) OVERRIDE
{
57 visible_fraction_
= visible_fraction
;
59 virtual std::vector
<gfx::Rect
> GetVisibleBoundsInScreen() const OVERRIDE
{
60 std::vector
<gfx::Rect
> bounds_in_screen
;
61 bounds_in_screen
.push_back(top_container_view_
->GetBoundsInScreen());
62 return bounds_in_screen
;
65 bool is_enabled() const {
69 double visible_fraction() const {
70 return visible_fraction_
;
74 views::View
* top_container_view_
;
76 double visible_fraction_
;
78 DISALLOW_COPY_AND_ASSIGN(MockImmersiveFullscreenControllerDelegate
);
81 class ConsumeEventHandler
: public ui::test::TestEventHandler
{
83 ConsumeEventHandler() {}
84 virtual ~ConsumeEventHandler() {}
87 virtual void OnEvent(ui::Event
* event
) OVERRIDE
{
88 ui::test::TestEventHandler::OnEvent(event
);
89 if (event
->cancelable())
93 DISALLOW_COPY_AND_ASSIGN(ConsumeEventHandler
);
98 /////////////////////////////////////////////////////////////////////////////
100 class ImmersiveFullscreenControllerTest
: public ash::test::AshTestBase
{
104 MODALITY_GESTURE_TAP
,
105 MODALITY_GESTURE_SCROLL
108 ImmersiveFullscreenControllerTest()
110 top_container_(NULL
),
111 content_view_(NULL
) {}
112 virtual ~ImmersiveFullscreenControllerTest() {}
114 ImmersiveFullscreenController
* controller() {
115 return controller_
.get();
118 views::NativeViewHost
* content_view() {
119 return content_view_
;
122 views::View
* top_container() {
123 return top_container_
;
126 views::Widget
* widget() { return widget_
; }
128 aura::Window
* window() {
129 return widget_
->GetNativeWindow();
132 MockImmersiveFullscreenControllerDelegate
* delegate() {
133 return delegate_
.get();
136 // Access to private data from the controller.
137 bool top_edge_hover_timer_running() const {
138 return controller_
->top_edge_hover_timer_
.IsRunning();
140 int mouse_x_when_hit_top() const {
141 return controller_
->mouse_x_when_hit_top_in_screen_
;
144 // ash::test::AshTestBase overrides:
145 virtual void SetUp() OVERRIDE
{
146 ash::test::AshTestBase::SetUp();
148 widget_
= new views::Widget();
149 views::Widget::InitParams params
;
150 params
.context
= CurrentContext();
151 widget_
->Init(params
);
154 window()->SetProperty(aura::client::kShowStateKey
,
155 ui::SHOW_STATE_FULLSCREEN
);
157 gfx::Size window_size
= widget_
->GetWindowBoundsInScreen().size();
158 content_view_
= new views::NativeViewHost();
159 content_view_
->SetBounds(0, 0, window_size
.width(), window_size
.height());
160 widget_
->GetContentsView()->AddChildView(content_view_
);
162 top_container_
= new views::View();
163 top_container_
->SetBounds(
164 0, 0, window_size
.width(), 100);
165 top_container_
->SetFocusable(true);
166 widget_
->GetContentsView()->AddChildView(top_container_
);
169 new MockImmersiveFullscreenControllerDelegate(top_container_
));
170 controller_
.reset(new ImmersiveFullscreenController
);
171 controller_
->Init(delegate_
.get(), widget_
, top_container_
);
172 controller_
->SetupForTest();
174 // The mouse is moved so that it is not over |top_container_| by
178 // Enables / disables immersive fullscreen.
179 void SetEnabled(bool enabled
) {
180 controller_
->SetEnabled(ImmersiveFullscreenController::WINDOW_TYPE_OTHER
,
184 // Attempt to reveal the top-of-window views via |modality|.
185 // The top-of-window views can only be revealed via mouse hover or a gesture.
186 void AttemptReveal(Modality modality
) {
187 ASSERT_NE(modality
, MODALITY_GESTURE_TAP
);
188 AttemptRevealStateChange(true, modality
);
191 // Attempt to unreveal the top-of-window views via |modality|. The
192 // top-of-window views can be unrevealed via any modality.
193 void AttemptUnreveal(Modality modality
) {
194 AttemptRevealStateChange(false, modality
);
197 // Sets whether the mouse is hovered above |top_container_|.
198 // SetHovered(true) moves the mouse over the |top_container_| but does not
199 // move it to the top of the screen so will not initiate a reveal.
200 void SetHovered(bool is_mouse_hovered
) {
201 MoveMouse(0, is_mouse_hovered
? 10 : top_container_
->height() + 100);
204 // Move the mouse to the given coordinates. The coordinates should be in
205 // |top_container_| coordinates.
206 void MoveMouse(int x
, int y
) {
207 gfx::Point
screen_position(x
, y
);
208 views::View::ConvertPointToScreen(top_container_
, &screen_position
);
209 GetEventGenerator().MoveMouseTo(screen_position
.x(), screen_position
.y());
211 // If the top edge timer started running as a result of the mouse move, run
212 // the task which occurs after the timer delay. This reveals the
213 // top-of-window views synchronously if the mouse is hovered at the top of
215 if (controller()->top_edge_hover_timer_
.IsRunning()) {
216 controller()->top_edge_hover_timer_
.user_task().Run();
217 controller()->top_edge_hover_timer_
.Stop();
222 // Attempt to change the revealed state to |revealed| via |modality|.
223 void AttemptRevealStateChange(bool revealed
, Modality modality
) {
224 // Compute the event position in |top_container_| coordinates.
225 gfx::Point
event_position(0, revealed
? 0 : top_container_
->height() + 100);
227 case MODALITY_MOUSE
: {
228 MoveMouse(event_position
.x(), event_position
.y());
231 case MODALITY_GESTURE_TAP
: {
232 gfx::Point screen_position
= event_position
;
233 views::View::ConvertPointToScreen(top_container_
, &screen_position
);
234 ui::test::EventGenerator
& event_generator(GetEventGenerator());
235 event_generator
.MoveTouch(event_position
);
236 event_generator
.PressTouch();
237 event_generator
.ReleaseTouch();
240 case MODALITY_GESTURE_SCROLL
: {
241 gfx::Point
start(0, revealed
? 0 : top_container_
->height() - 2);
242 gfx::Vector2d
scroll_delta(0, 40);
243 gfx::Point end
= revealed
? start
+ scroll_delta
: start
- scroll_delta
;
244 views::View::ConvertPointToScreen(top_container_
, &start
);
245 views::View::ConvertPointToScreen(top_container_
, &end
);
246 ui::test::EventGenerator
& event_generator(GetEventGenerator());
247 event_generator
.GestureScrollSequence(
249 base::TimeDelta::FromMilliseconds(30), 1);
255 scoped_ptr
<ImmersiveFullscreenController
> controller_
;
256 scoped_ptr
<MockImmersiveFullscreenControllerDelegate
> delegate_
;
257 views::Widget
* widget_
; // Owned by the native widget.
258 views::View
* top_container_
; // Owned by |widget_|'s root-view.
259 views::NativeViewHost
* content_view_
; // Owned by |widget_|'s root-view.
261 DISALLOW_COPY_AND_ASSIGN(ImmersiveFullscreenControllerTest
);
264 // Test the initial state and that the delegate gets notified of the
265 // top-of-window views getting hidden and revealed.
266 TEST_F(ImmersiveFullscreenControllerTest
, Delegate
) {
268 EXPECT_FALSE(controller()->IsEnabled());
269 EXPECT_FALSE(controller()->IsRevealed());
270 EXPECT_FALSE(delegate()->is_enabled());
272 // Enabling initially hides the top views.
274 EXPECT_TRUE(controller()->IsEnabled());
275 EXPECT_FALSE(controller()->IsRevealed());
276 EXPECT_TRUE(delegate()->is_enabled());
277 EXPECT_EQ(0, delegate()->visible_fraction());
279 // Revealing shows the top views.
280 AttemptReveal(MODALITY_MOUSE
);
281 EXPECT_TRUE(controller()->IsEnabled());
282 EXPECT_TRUE(controller()->IsRevealed());
283 EXPECT_TRUE(delegate()->is_enabled());
284 EXPECT_EQ(1, delegate()->visible_fraction());
286 // Disabling ends the immersive reveal.
288 EXPECT_FALSE(controller()->IsEnabled());
289 EXPECT_FALSE(controller()->IsRevealed());
290 EXPECT_FALSE(delegate()->is_enabled());
293 // GetRevealedLock() specific tests.
294 TEST_F(ImmersiveFullscreenControllerTest
, RevealedLock
) {
295 scoped_ptr
<ImmersiveRevealedLock
> lock1
;
296 scoped_ptr
<ImmersiveRevealedLock
> lock2
;
298 // Immersive fullscreen is not on by default.
299 EXPECT_FALSE(controller()->IsEnabled());
301 // 1) Test acquiring and releasing a revealed state lock while immersive
302 // fullscreen is disabled. Acquiring or releasing the lock should have no
303 // effect till immersive fullscreen is enabled.
304 lock1
.reset(controller()->GetRevealedLock(
305 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
306 EXPECT_FALSE(controller()->IsEnabled());
307 EXPECT_FALSE(controller()->IsRevealed());
309 // Immersive fullscreen should start in the revealed state due to the lock.
311 EXPECT_TRUE(controller()->IsEnabled());
312 EXPECT_TRUE(controller()->IsRevealed());
315 EXPECT_FALSE(controller()->IsEnabled());
316 EXPECT_FALSE(controller()->IsRevealed());
319 EXPECT_FALSE(controller()->IsEnabled());
320 EXPECT_FALSE(controller()->IsRevealed());
322 // Immersive fullscreen should start in the closed state because the lock is
325 EXPECT_TRUE(controller()->IsEnabled());
326 EXPECT_FALSE(controller()->IsRevealed());
328 // 2) Test that acquiring a lock reveals the top-of-window views if they are
330 lock1
.reset(controller()->GetRevealedLock(
331 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
332 EXPECT_TRUE(controller()->IsRevealed());
334 // 3) Test that the top-of-window views are only hidden when all of the locks
336 lock2
.reset(controller()->GetRevealedLock(
337 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
339 EXPECT_TRUE(controller()->IsRevealed());
342 EXPECT_FALSE(controller()->IsRevealed());
345 // Test mouse event processing for top-of-screen reveal triggering.
346 TEST_F(ImmersiveFullscreenControllerTest
, OnMouseEvent
) {
347 // Set up initial state.
349 ASSERT_TRUE(controller()->IsEnabled());
350 ASSERT_FALSE(controller()->IsRevealed());
352 ui::test::EventGenerator
& event_generator(GetEventGenerator());
354 gfx::Rect top_container_bounds_in_screen
=
355 top_container()->GetBoundsInScreen();
356 // A position along the top edge of TopContainerView in screen coordinates.
357 gfx::Point
top_edge_pos(top_container_bounds_in_screen
.x() + 100,
358 top_container_bounds_in_screen
.y());
360 // Mouse wheel event does nothing.
361 ui::MouseEvent
wheel(
362 ui::ET_MOUSEWHEEL
, top_edge_pos
, top_edge_pos
, ui::EF_NONE
, ui::EF_NONE
);
363 event_generator
.Dispatch(&wheel
);
364 EXPECT_FALSE(top_edge_hover_timer_running());
366 // Move to top edge of screen starts hover timer running. We cannot use
367 // MoveMouse() because MoveMouse() stops the timer if it started running.
368 event_generator
.MoveMouseTo(top_edge_pos
);
369 EXPECT_TRUE(top_edge_hover_timer_running());
370 EXPECT_EQ(top_edge_pos
.x(), mouse_x_when_hit_top());
372 // Moving |ImmersiveFullscreenControllerTest::kMouseRevealBoundsHeight| down
373 // from the top edge stops it.
374 event_generator
.MoveMouseBy(0,
375 ImmersiveFullscreenController::kMouseRevealBoundsHeight
);
376 EXPECT_FALSE(top_edge_hover_timer_running());
378 // Moving back to the top starts the timer again.
379 event_generator
.MoveMouseTo(top_edge_pos
);
380 EXPECT_TRUE(top_edge_hover_timer_running());
381 EXPECT_EQ(top_edge_pos
.x(), mouse_x_when_hit_top());
383 // Slight move to the right keeps the timer running for the same hit point.
384 event_generator
.MoveMouseBy(1, 0);
385 EXPECT_TRUE(top_edge_hover_timer_running());
386 EXPECT_EQ(top_edge_pos
.x(), mouse_x_when_hit_top());
388 // Moving back to the left also keeps the timer running.
389 event_generator
.MoveMouseBy(-1, 0);
390 EXPECT_TRUE(top_edge_hover_timer_running());
391 EXPECT_EQ(top_edge_pos
.x(), mouse_x_when_hit_top());
393 // Large move right restarts the timer (so it is still running) and considers
394 // this a new hit at the top.
395 event_generator
.MoveMouseTo(top_edge_pos
.x() + 100, top_edge_pos
.y());
396 EXPECT_TRUE(top_edge_hover_timer_running());
397 EXPECT_EQ(top_edge_pos
.x() + 100, mouse_x_when_hit_top());
399 // Moving off the top edge horizontally stops the timer.
400 event_generator
.MoveMouseTo(top_container_bounds_in_screen
.right() + 1,
401 top_container_bounds_in_screen
.y());
402 EXPECT_FALSE(top_edge_hover_timer_running());
404 // Once revealed, a move just a little below the top container doesn't end a
406 AttemptReveal(MODALITY_MOUSE
);
407 event_generator
.MoveMouseTo(top_container_bounds_in_screen
.x(),
408 top_container_bounds_in_screen
.bottom() + 1);
409 EXPECT_TRUE(controller()->IsRevealed());
411 // Once revealed, clicking just below the top container ends the reveal.
412 event_generator
.ClickLeftButton();
413 EXPECT_FALSE(controller()->IsRevealed());
415 // Moving a lot below the top container ends a reveal.
416 AttemptReveal(MODALITY_MOUSE
);
417 EXPECT_TRUE(controller()->IsRevealed());
418 event_generator
.MoveMouseTo(top_container_bounds_in_screen
.x(),
419 top_container_bounds_in_screen
.bottom() + 50);
420 EXPECT_FALSE(controller()->IsRevealed());
422 // The mouse position cannot cause a reveal when the top container's widget
424 views::Widget
* widget
= top_container()->GetWidget();
425 widget
->SetCapture(top_container());
426 AttemptReveal(MODALITY_MOUSE
);
427 EXPECT_FALSE(controller()->IsRevealed());
428 widget
->ReleaseCapture();
430 // The mouse position cannot end the reveal while the top container's widget
432 AttemptReveal(MODALITY_MOUSE
);
433 EXPECT_TRUE(controller()->IsRevealed());
434 widget
->SetCapture(top_container());
435 event_generator
.MoveMouseTo(top_container_bounds_in_screen
.x(),
436 top_container_bounds_in_screen
.bottom() + 51);
437 EXPECT_TRUE(controller()->IsRevealed());
439 // Releasing capture should end the reveal.
440 widget
->ReleaseCapture();
441 EXPECT_FALSE(controller()->IsRevealed());
444 // Test mouse event processing for top-of-screen reveal triggering when the
445 // top container's widget is inactive.
446 TEST_F(ImmersiveFullscreenControllerTest
, Inactive
) {
447 // Set up initial state.
448 views::Widget
* popup_widget
= views::Widget::CreateWindowWithContextAndBounds(
451 gfx::Rect(0, 0, 200, 200));
452 popup_widget
->Show();
453 ASSERT_FALSE(top_container()->GetWidget()->IsActive());
456 ASSERT_TRUE(controller()->IsEnabled());
457 ASSERT_FALSE(controller()->IsRevealed());
459 gfx::Rect top_container_bounds_in_screen
=
460 top_container()->GetBoundsInScreen();
461 gfx::Rect popup_bounds_in_screen
= popup_widget
->GetWindowBoundsInScreen();
462 ASSERT_EQ(top_container_bounds_in_screen
.origin().ToString(),
463 popup_bounds_in_screen
.origin().ToString());
464 ASSERT_GT(top_container_bounds_in_screen
.right(),
465 popup_bounds_in_screen
.right());
467 // The top-of-window views should stay hidden if the cursor is at the top edge
468 // but above an obscured portion of the top-of-window views.
469 MoveMouse(popup_bounds_in_screen
.x(),
470 top_container_bounds_in_screen
.y());
471 EXPECT_FALSE(controller()->IsRevealed());
473 // The top-of-window views should reveal if the cursor is at the top edge and
474 // above an unobscured portion of the top-of-window views.
475 MoveMouse(top_container_bounds_in_screen
.right() - 1,
476 top_container_bounds_in_screen
.y());
477 EXPECT_TRUE(controller()->IsRevealed());
479 // The top-of-window views should stay revealed if the cursor is moved off
481 MoveMouse(top_container_bounds_in_screen
.right() - 1,
482 top_container_bounds_in_screen
.bottom() - 1);
483 EXPECT_TRUE(controller()->IsRevealed());
485 // Moving way off of the top-of-window views should end the immersive reveal.
486 MoveMouse(top_container_bounds_in_screen
.right() - 1,
487 top_container_bounds_in_screen
.bottom() + 50);
488 EXPECT_FALSE(controller()->IsRevealed());
490 // Moving way off of the top-of-window views in a region where the
491 // top-of-window views are obscured should also end the immersive reveal.
492 // Ideally, the immersive reveal would end immediately when the cursor moves
493 // to an obscured portion of the top-of-window views.
494 MoveMouse(top_container_bounds_in_screen
.right() - 1,
495 top_container_bounds_in_screen
.y());
496 EXPECT_TRUE(controller()->IsRevealed());
497 MoveMouse(top_container_bounds_in_screen
.x(),
498 top_container_bounds_in_screen
.bottom() + 50);
499 EXPECT_FALSE(controller()->IsRevealed());
502 // Test mouse event processing for top-of-screen reveal triggering when the user
503 // has a vertical display layout (primary display above/below secondary display)
504 // and the immersive fullscreen window is on the bottom display.
505 TEST_F(ImmersiveFullscreenControllerTest
, MouseEventsVerticalDisplayLayout
) {
506 if (!SupportsMultipleDisplays())
509 // Set up initial state.
510 UpdateDisplay("800x600,800x600");
511 ash::DisplayLayout
display_layout(ash::DisplayLayout::TOP
, 0);
512 ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays(
516 ASSERT_TRUE(controller()->IsEnabled());
517 ASSERT_FALSE(controller()->IsRevealed());
519 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
520 ASSERT_EQ(root_windows
[0],
521 top_container()->GetWidget()->GetNativeWindow()->GetRootWindow());
523 gfx::Rect primary_root_window_bounds_in_screen
=
524 root_windows
[0]->GetBoundsInScreen();
525 // Do not set |x| to the root window's x position because the display's
526 // corners have special behavior.
527 int x
= primary_root_window_bounds_in_screen
.x() + 10;
528 // The y position of the top edge of the primary display.
529 int y_top_edge
= primary_root_window_bounds_in_screen
.y();
531 ui::test::EventGenerator
& event_generator(GetEventGenerator());
533 // Moving right below the top edge starts the hover timer running. We
534 // cannot use MoveMouse() because MoveMouse() stops the timer if it started
536 event_generator
.MoveMouseTo(x
, y_top_edge
+ 1);
537 EXPECT_TRUE(top_edge_hover_timer_running());
538 EXPECT_EQ(y_top_edge
+ 1,
539 aura::Env::GetInstance()->last_mouse_location().y());
541 // The timer should continue running if the user moves the mouse to the top
542 // edge even though the mouse is warped to the secondary display.
543 event_generator
.MoveMouseTo(x
, y_top_edge
);
544 EXPECT_TRUE(top_edge_hover_timer_running());
546 // TODO(oshima): Provide a test API to handle mouse warp more easily.
547 #if defined(USE_OZONE)
548 EXPECT_NE(y_top_edge
, aura::Env::GetInstance()->last_mouse_location().y());
551 // The timer should continue running if the user overshoots the top edge
553 event_generator
.MoveMouseTo(x
, y_top_edge
- 2);
554 EXPECT_TRUE(top_edge_hover_timer_running());
556 // The timer should stop running if the user overshoots the top edge by
558 event_generator
.MoveMouseTo(x
, y_top_edge
- 20);
559 EXPECT_FALSE(top_edge_hover_timer_running());
561 // The timer should not start if the user moves the mouse to the bottom of the
562 // secondary display without crossing the top edge first.
563 event_generator
.MoveMouseTo(x
, y_top_edge
- 2);
565 // Reveal the top-of-window views by overshooting the top edge slightly.
566 event_generator
.MoveMouseTo(x
, y_top_edge
+ 1);
567 // MoveMouse() runs the timer task.
568 MoveMouse(x
, y_top_edge
- 2);
569 EXPECT_TRUE(controller()->IsRevealed());
571 // The top-of-window views should stay revealed if the user moves the mouse
572 // around in the bottom region of the secondary display.
573 event_generator
.MoveMouseTo(x
+ 10, y_top_edge
- 3);
574 EXPECT_TRUE(controller()->IsRevealed());
576 // The top-of-window views should hide if the user moves the mouse away from
577 // the bottom region of the secondary display.
578 event_generator
.MoveMouseTo(x
, y_top_edge
- 20);
579 EXPECT_FALSE(controller()->IsRevealed());
581 // Test that it is possible to reveal the top-of-window views by overshooting
582 // the top edge slightly when the top container's widget is not active.
583 views::Widget
* popup_widget
= views::Widget::CreateWindowWithContextAndBounds(
586 gfx::Rect(0, 200, 100, 100));
587 popup_widget
->Show();
588 ASSERT_FALSE(top_container()->GetWidget()->IsActive());
589 ASSERT_FALSE(top_container()->GetBoundsInScreen().Intersects(
590 popup_widget
->GetWindowBoundsInScreen()));
591 event_generator
.MoveMouseTo(x
, y_top_edge
+ 1);
592 MoveMouse(x
, y_top_edge
- 2);
593 EXPECT_TRUE(controller()->IsRevealed());
596 // Test behavior when the mouse becomes hovered without moving.
597 TEST_F(ImmersiveFullscreenControllerTest
, MouseHoveredWithoutMoving
) {
599 scoped_ptr
<ImmersiveRevealedLock
> lock
;
601 // 1) Test that if the mouse becomes hovered without the mouse moving due to a
602 // lock causing the top-of-window views to be revealed (and the mouse
603 // happening to be near the top of the screen), the top-of-window views do not
604 // hide till the mouse moves off of the top-of-window views.
606 EXPECT_FALSE(controller()->IsRevealed());
607 lock
.reset(controller()->GetRevealedLock(
608 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
609 EXPECT_TRUE(controller()->IsRevealed());
611 EXPECT_TRUE(controller()->IsRevealed());
613 EXPECT_FALSE(controller()->IsRevealed());
615 // 2) Test that if the mouse becomes hovered without moving because of a
616 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there are no
617 // locks keeping the top-of-window views revealed, that mouse hover does not
618 // prevent the top-of-window views from closing.
621 EXPECT_FALSE(controller()->IsRevealed());
623 EXPECT_FALSE(controller()->IsRevealed());
625 // 3) Test that if the mouse becomes hovered without moving because of a
626 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there is a
627 // lock keeping the top-of-window views revealed, that the top-of-window views
628 // do not hide till the mouse moves off of the top-of-window views.
631 lock
.reset(controller()->GetRevealedLock(
632 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
633 EXPECT_FALSE(controller()->IsRevealed());
635 EXPECT_TRUE(controller()->IsRevealed());
637 EXPECT_TRUE(controller()->IsRevealed());
639 EXPECT_FALSE(controller()->IsRevealed());
642 // Test revealing the top-of-window views using one modality and ending
643 // the reveal via another. For instance, initiating the reveal via a SWIPE_OPEN
644 // edge gesture, switching to using the mouse and ending the reveal by moving
645 // the mouse off of the top-of-window views.
646 TEST_F(ImmersiveFullscreenControllerTest
, DifferentModalityEnterExit
) {
648 EXPECT_TRUE(controller()->IsEnabled());
649 EXPECT_FALSE(controller()->IsRevealed());
651 // Initiate reveal via gesture, end reveal via mouse.
652 AttemptReveal(MODALITY_GESTURE_SCROLL
);
653 EXPECT_TRUE(controller()->IsRevealed());
655 EXPECT_TRUE(controller()->IsRevealed());
656 AttemptUnreveal(MODALITY_MOUSE
);
657 EXPECT_FALSE(controller()->IsRevealed());
659 // Initiate reveal via gesture, end reveal via touch.
660 AttemptReveal(MODALITY_GESTURE_SCROLL
);
661 EXPECT_TRUE(controller()->IsRevealed());
662 AttemptUnreveal(MODALITY_GESTURE_TAP
);
663 EXPECT_FALSE(controller()->IsRevealed());
665 // Initiate reveal via mouse, end reveal via gesture.
666 AttemptReveal(MODALITY_MOUSE
);
667 EXPECT_TRUE(controller()->IsRevealed());
668 AttemptUnreveal(MODALITY_GESTURE_SCROLL
);
669 EXPECT_FALSE(controller()->IsRevealed());
671 // Initiate reveal via mouse, end reveal via touch.
672 AttemptReveal(MODALITY_MOUSE
);
673 EXPECT_TRUE(controller()->IsRevealed());
674 AttemptUnreveal(MODALITY_GESTURE_TAP
);
675 EXPECT_FALSE(controller()->IsRevealed());
678 // Test when the SWIPE_CLOSE edge gesture closes the top-of-window views.
680 // On Windows, touch events do not result in mouse events being disabled. As
681 // a result, the last part of this test which ends the reveal via a gesture will
682 // not work correctly. See crbug.com/332430, and the function
683 // ShouldHideCursorOnTouch() in compound_event_filter.cc.
684 #define MAYBE_EndRevealViaGesture DISABLED_EndRevealViaGesture
686 #define MAYBE_EndRevealViaGesture EndRevealViaGesture
688 TEST_F(ImmersiveFullscreenControllerTest
, MAYBE_EndRevealViaGesture
) {
690 EXPECT_TRUE(controller()->IsEnabled());
691 EXPECT_FALSE(controller()->IsRevealed());
693 // A gesture should be able to close the top-of-window views when
694 // top-of-window views have focus.
695 AttemptReveal(MODALITY_MOUSE
);
696 top_container()->RequestFocus();
697 EXPECT_TRUE(controller()->IsRevealed());
698 AttemptUnreveal(MODALITY_GESTURE_SCROLL
);
699 EXPECT_FALSE(controller()->IsRevealed());
701 // The top-of-window views should no longer have focus. Clearing focus is
702 // important because it closes focus-related popup windows like the touch
703 // selection handles.
704 EXPECT_FALSE(top_container()->HasFocus());
706 // If some other code is holding onto a lock, a gesture should not be able to
708 AttemptReveal(MODALITY_MOUSE
);
709 scoped_ptr
<ImmersiveRevealedLock
> lock(controller()->GetRevealedLock(
710 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
711 EXPECT_TRUE(controller()->IsRevealed());
712 AttemptUnreveal(MODALITY_GESTURE_SCROLL
);
713 EXPECT_TRUE(controller()->IsRevealed());
715 EXPECT_FALSE(controller()->IsRevealed());
718 // Tests that touch-gesture can be used to reveal the top-of-window views when
719 // the child window consumes all events.
720 TEST_F(ImmersiveFullscreenControllerTest
, RevealViaGestureChildConsumesEvents
) {
721 // Enabling initially hides the top views.
723 EXPECT_TRUE(controller()->IsEnabled());
724 EXPECT_FALSE(controller()->IsRevealed());
726 aura::test::TestWindowDelegate child_delegate
;
727 scoped_ptr
<aura::Window
> child(
728 CreateTestWindowInShellWithDelegateAndType(&child_delegate
,
729 ui::wm::WINDOW_TYPE_CONTROL
,
732 content_view()->Attach(child
.get());
735 ConsumeEventHandler handler
;
736 child
->AddPreTargetHandler(&handler
);
738 // Reveal the top views using a touch-scroll gesture. The child window should
739 // not receive the touch events.
740 AttemptReveal(MODALITY_GESTURE_SCROLL
);
741 EXPECT_TRUE(controller()->IsRevealed());
742 EXPECT_EQ(0, handler
.num_touch_events());
744 AttemptUnreveal(MODALITY_GESTURE_TAP
);
745 EXPECT_FALSE(controller()->IsRevealed());
746 EXPECT_GT(handler
.num_touch_events(), 0);
747 child
->RemovePreTargetHandler(&handler
);
750 // Make sure touch events towards the top of the window do not leak through to
751 // windows underneath.
752 TEST_F(ImmersiveFullscreenControllerTest
, EventsDoNotLeakToWindowUnderneath
) {
753 gfx::Rect window_bounds
= window()->GetBoundsInScreen();
754 aura::test::TestWindowDelegate child_delegate
;
755 scoped_ptr
<aura::Window
> behind(CreateTestWindowInShellWithDelegate(
756 &child_delegate
, 1234, window_bounds
));
758 behind
->SetBounds(window_bounds
);
759 widget()->StackAbove(behind
.get());
761 // Make sure the windows are aligned on top.
762 EXPECT_EQ(behind
->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
763 int top
= behind
->GetBoundsInScreen().y();
765 ui::TouchEvent
touch(ui::ET_TOUCH_MOVED
, gfx::Point(10, top
), 0,
766 ui::EventTimeForNow());
767 ui::EventTarget
* root
= window()->GetRootWindow();
768 ui::EventTargeter
* targeter
= root
->GetEventTargeter();
769 EXPECT_EQ(window(), targeter
->FindTargetForEvent(root
, &touch
));
772 EXPECT_FALSE(controller()->IsRevealed());
773 // Make sure the windows are still aligned on top.
774 EXPECT_EQ(behind
->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
775 top
= behind
->GetBoundsInScreen().y();
776 ui::TouchEvent
touch2(ui::ET_TOUCH_MOVED
, gfx::Point(10, top
), 0,
777 ui::EventTimeForNow());
778 // The event should still be targeted to window().
779 EXPECT_EQ(window(), targeter
->FindTargetForEvent(root
, &touch2
));
782 // Check that the window state gets properly marked for immersive fullscreen.
783 TEST_F(ImmersiveFullscreenControllerTest
, WindowStateImmersiveFullscreen
) {
784 ash::wm::WindowState
* window_state
= ash::wm::GetWindowState(window());
786 EXPECT_FALSE(window_state
->in_immersive_fullscreen());
788 ASSERT_TRUE(controller()->IsEnabled());
789 EXPECT_TRUE(window_state
->in_immersive_fullscreen());
792 ASSERT_FALSE(controller()->IsEnabled());
793 EXPECT_FALSE(window_state
->in_immersive_fullscreen());
796 // Do not test under windows because focus testing is not reliable on
797 // Windows. (crbug.com/79493)
800 // Test how focus and activation affects whether the top-of-window views are
802 TEST_F(ImmersiveFullscreenControllerTest
, Focus
) {
803 // Add views to the view hierarchy which we will focus and unfocus during the
805 views::View
* child_view
= new views::View();
806 child_view
->SetBounds(0, 0, 10, 10);
807 child_view
->SetFocusable(true);
808 top_container()->AddChildView(child_view
);
809 views::View
* unrelated_view
= new views::View();
810 unrelated_view
->SetBounds(0, 100, 10, 10);
811 unrelated_view
->SetFocusable(true);
812 top_container()->parent()->AddChildView(unrelated_view
);
813 views::FocusManager
* focus_manager
=
814 top_container()->GetWidget()->GetFocusManager();
818 // 1) Test that the top-of-window views stay revealed as long as either a
819 // |child_view| has focus or the mouse is hovered above the top-of-window
821 AttemptReveal(MODALITY_MOUSE
);
822 child_view
->RequestFocus();
823 focus_manager
->ClearFocus();
824 EXPECT_TRUE(controller()->IsRevealed());
825 child_view
->RequestFocus();
827 EXPECT_TRUE(controller()->IsRevealed());
828 focus_manager
->ClearFocus();
829 EXPECT_FALSE(controller()->IsRevealed());
831 // 2) Test that focusing |unrelated_view| hides the top-of-window views.
832 // Note: In this test we can cheat and trigger a reveal via focus because
833 // the top container does not hide when the top-of-window views are not
835 child_view
->RequestFocus();
836 EXPECT_TRUE(controller()->IsRevealed());
837 unrelated_view
->RequestFocus();
838 EXPECT_FALSE(controller()->IsRevealed());
840 // 3) Test that a loss of focus of |child_view| to |unrelated_view|
841 // while immersive mode is disabled is properly registered.
842 child_view
->RequestFocus();
843 EXPECT_TRUE(controller()->IsRevealed());
845 EXPECT_FALSE(controller()->IsRevealed());
846 unrelated_view
->RequestFocus();
848 EXPECT_FALSE(controller()->IsRevealed());
850 // Repeat test but with a revealed lock acquired when immersive mode is
851 // disabled because the code path is different.
852 child_view
->RequestFocus();
853 EXPECT_TRUE(controller()->IsRevealed());
855 scoped_ptr
<ImmersiveRevealedLock
> lock(controller()->GetRevealedLock(
856 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
857 EXPECT_FALSE(controller()->IsRevealed());
858 unrelated_view
->RequestFocus();
860 EXPECT_TRUE(controller()->IsRevealed());
862 EXPECT_FALSE(controller()->IsRevealed());
865 // Test how transient windows affect whether the top-of-window views are
867 TEST_F(ImmersiveFullscreenControllerTest
, Transient
) {
868 views::Widget
* top_container_widget
= top_container()->GetWidget();
871 ASSERT_FALSE(controller()->IsRevealed());
873 // 1) Test that a transient window which is not a bubble does not trigger a
874 // reveal but does keep the top-of-window views revealed if they are already
876 views::Widget::InitParams transient_params
;
877 transient_params
.ownership
=
878 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
879 transient_params
.parent
= top_container_widget
->GetNativeView();
880 transient_params
.bounds
= gfx::Rect(0, 100, 100, 100);
881 scoped_ptr
<views::Widget
> transient_widget(new views::Widget());
882 transient_widget
->Init(transient_params
);
884 EXPECT_FALSE(controller()->IsRevealed());
885 AttemptReveal(MODALITY_MOUSE
);
886 EXPECT_TRUE(controller()->IsRevealed());
887 transient_widget
->Show();
889 EXPECT_TRUE(controller()->IsRevealed());
890 transient_widget
.reset();
891 EXPECT_FALSE(controller()->IsRevealed());
893 // 2) Test that activating a non-transient window does not keep the
894 // top-of-window views revealed.
895 views::Widget::InitParams non_transient_params
;
896 non_transient_params
.ownership
=
897 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
898 non_transient_params
.context
= top_container_widget
->GetNativeView();
899 non_transient_params
.bounds
= gfx::Rect(0, 100, 100, 100);
900 scoped_ptr
<views::Widget
> non_transient_widget(new views::Widget());
901 non_transient_widget
->Init(non_transient_params
);
903 EXPECT_FALSE(controller()->IsRevealed());
904 AttemptReveal(MODALITY_MOUSE
);
905 EXPECT_TRUE(controller()->IsRevealed());
906 non_transient_widget
->Show();
908 EXPECT_FALSE(controller()->IsRevealed());
911 // Test how bubbles affect whether the top-of-window views are revealed.
912 TEST_F(ImmersiveFullscreenControllerTest
, Bubbles
) {
913 scoped_ptr
<ImmersiveRevealedLock
> revealed_lock
;
914 views::Widget
* top_container_widget
= top_container()->GetWidget();
916 // Add views to the view hierarchy to which we will anchor bubbles.
917 views::View
* child_view
= new views::View();
918 child_view
->SetBounds(0, 0, 10, 10);
919 top_container()->AddChildView(child_view
);
920 views::View
* unrelated_view
= new views::View();
921 unrelated_view
->SetBounds(0, 100, 10, 10);
922 top_container()->parent()->AddChildView(unrelated_view
);
925 ASSERT_FALSE(controller()->IsRevealed());
927 // 1) Test that a bubble anchored to a child of the top container triggers
928 // a reveal and keeps the top-of-window views revealed for the duration of
930 views::Widget
* bubble_widget1(views::BubbleDelegateView::CreateBubble(
931 new views::BubbleDelegateView(child_view
, views::BubbleBorder::NONE
)));
932 bubble_widget1
->Show();
933 EXPECT_TRUE(controller()->IsRevealed());
935 // Activating |top_container_widget| will close |bubble_widget1|.
936 top_container_widget
->Activate();
937 AttemptReveal(MODALITY_MOUSE
);
938 revealed_lock
.reset(controller()->GetRevealedLock(
939 ImmersiveFullscreenController::ANIMATE_REVEAL_NO
));
940 EXPECT_TRUE(controller()->IsRevealed());
942 views::Widget
* bubble_widget2
= views::BubbleDelegateView::CreateBubble(
943 new views::BubbleDelegateView(child_view
, views::BubbleBorder::NONE
));
944 bubble_widget2
->Show();
945 EXPECT_TRUE(controller()->IsRevealed());
946 revealed_lock
.reset();
948 EXPECT_TRUE(controller()->IsRevealed());
949 bubble_widget2
->Close();
950 EXPECT_FALSE(controller()->IsRevealed());
952 // 2) Test that transitioning from keeping the top-of-window views revealed
953 // because of a bubble to keeping the top-of-window views revealed because of
954 // mouse hover by activating |top_container_widget| works.
955 views::Widget
* bubble_widget3
= views::BubbleDelegateView::CreateBubble(
956 new views::BubbleDelegateView(child_view
, views::BubbleBorder::NONE
));
957 bubble_widget3
->Show();
959 EXPECT_TRUE(controller()->IsRevealed());
960 top_container_widget
->Activate();
961 EXPECT_TRUE(controller()->IsRevealed());
963 // 3) Test that the top-of-window views stay revealed as long as at least one
964 // bubble anchored to a child of the top container is visible.
966 EXPECT_FALSE(controller()->IsRevealed());
968 views::BubbleDelegateView
* bubble_delegate4(new views::BubbleDelegateView(
969 child_view
, views::BubbleBorder::NONE
));
970 bubble_delegate4
->set_can_activate(false);
971 views::Widget
* bubble_widget4(views::BubbleDelegateView::CreateBubble(
973 bubble_widget4
->Show();
975 views::BubbleDelegateView
* bubble_delegate5(new views::BubbleDelegateView(
976 child_view
, views::BubbleBorder::NONE
));
977 bubble_delegate5
->set_can_activate(false);
978 views::Widget
* bubble_widget5(views::BubbleDelegateView::CreateBubble(
980 bubble_widget5
->Show();
982 EXPECT_TRUE(controller()->IsRevealed());
983 bubble_widget4
->Hide();
984 EXPECT_TRUE(controller()->IsRevealed());
985 bubble_widget5
->Hide();
986 EXPECT_FALSE(controller()->IsRevealed());
987 bubble_widget5
->Show();
988 EXPECT_TRUE(controller()->IsRevealed());
990 // 4) Test that visibility changes which occur while immersive fullscreen is
991 // disabled are handled upon reenabling immersive fullscreen.
993 bubble_widget5
->Hide();
995 EXPECT_FALSE(controller()->IsRevealed());
997 // We do not need |bubble_widget4| or |bubble_widget5| anymore, close them.
998 bubble_widget4
->Close();
999 bubble_widget5
->Close();
1001 // 5) Test that a bubble added while immersive fullscreen is disabled is
1002 // handled upon reenabling immersive fullscreen.
1005 views::Widget
* bubble_widget6
= views::BubbleDelegateView::CreateBubble(
1006 new views::BubbleDelegateView(child_view
, views::BubbleBorder::NONE
));
1007 bubble_widget6
->Show();
1010 EXPECT_TRUE(controller()->IsRevealed());
1012 bubble_widget6
->Close();
1014 // 6) Test that a bubble which is not anchored to a child of the
1015 // TopContainerView does not trigger a reveal or keep the
1016 // top-of-window views revealed if they are already revealed.
1017 views::Widget
* bubble_widget7
= views::BubbleDelegateView::CreateBubble(
1018 new views::BubbleDelegateView(unrelated_view
, views::BubbleBorder::NONE
));
1019 bubble_widget7
->Show();
1020 EXPECT_FALSE(controller()->IsRevealed());
1022 // Activating |top_container_widget| will close |bubble_widget6|.
1023 top_container_widget
->Activate();
1024 AttemptReveal(MODALITY_MOUSE
);
1025 EXPECT_TRUE(controller()->IsRevealed());
1027 views::Widget
* bubble_widget8
= views::BubbleDelegateView::CreateBubble(
1028 new views::BubbleDelegateView(unrelated_view
, views::BubbleBorder::NONE
));
1029 bubble_widget8
->Show();
1031 EXPECT_FALSE(controller()->IsRevealed());
1032 bubble_widget8
->Close();
1035 #endif // defined(OS_WIN)
1037 // Test that the shelf is set to auto hide as long as the window is in
1038 // immersive fullscreen and that the shelf's state before entering immersive
1039 // fullscreen is restored upon exiting immersive fullscreen.
1040 TEST_F(ImmersiveFullscreenControllerTest
, Shelf
) {
1041 ash::ShelfLayoutManager
* shelf
=
1042 ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
1044 // Shelf is visible by default.
1045 window()->SetProperty(aura::client::kShowStateKey
, ui::SHOW_STATE_NORMAL
);
1046 ASSERT_FALSE(controller()->IsEnabled());
1047 ASSERT_EQ(ash::SHELF_VISIBLE
, shelf
->visibility_state());
1049 // Entering immersive fullscreen sets the shelf to auto hide.
1050 window()->SetProperty(aura::client::kShowStateKey
, ui::SHOW_STATE_FULLSCREEN
);
1052 EXPECT_EQ(ash::SHELF_AUTO_HIDE
, shelf
->visibility_state());
1054 // Disabling immersive fullscreen puts it back.
1056 window()->SetProperty(aura::client::kShowStateKey
, ui::SHOW_STATE_NORMAL
);
1057 ASSERT_FALSE(controller()->IsEnabled());
1058 EXPECT_EQ(ash::SHELF_VISIBLE
, shelf
->visibility_state());
1060 // The user could toggle the shelf auto-hide behavior.
1061 shelf
->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
);
1062 EXPECT_EQ(ash::SHELF_AUTO_HIDE
, shelf
->visibility_state());
1064 // Entering immersive fullscreen keeps auto-hide.
1065 window()->SetProperty(aura::client::kShowStateKey
, ui::SHOW_STATE_FULLSCREEN
);
1067 EXPECT_EQ(ash::SHELF_AUTO_HIDE
, shelf
->visibility_state());
1069 // Disabling immersive fullscreen maintains the user's auto-hide selection.
1071 window()->SetProperty(aura::client::kShowStateKey
,
1072 ui::SHOW_STATE_NORMAL
);
1073 EXPECT_EQ(ash::SHELF_AUTO_HIDE
, shelf
->visibility_state());