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/root_window_controller.h"
9 #include "ash/shelf/shelf_layout_manager.h"
10 #include "ash/shelf/shelf_types.h"
11 #include "ash/shell.h"
12 #include "ash/test/ash_test_base.h"
13 #include "ui/aura/client/aura_constants.h"
14 #include "ui/aura/client/cursor_client.h"
15 #include "ui/aura/env.h"
16 #include "ui/aura/root_window.h"
17 #include "ui/aura/test/event_generator.h"
18 #include "ui/aura/test/test_event_handler.h"
19 #include "ui/aura/test/test_window_delegate.h"
20 #include "ui/aura/window.h"
21 #include "ui/events/event_utils.h"
22 #include "ui/gfx/animation/slide_animation.h"
23 #include "ui/views/bubble/bubble_delegate.h"
24 #include "ui/views/controls/native/native_view_host.h"
25 #include "ui/views/view.h"
26 #include "ui/views/widget/widget.h"
28 // For now, immersive fullscreen is Chrome OS only.
29 #if defined(OS_CHROMEOS)
35 class MockImmersiveFullscreenControllerDelegate
36 : public ImmersiveFullscreenController::Delegate {
38 MockImmersiveFullscreenControllerDelegate(views::View* top_container_view)
39 : top_container_view_(top_container_view),
41 visible_fraction_(1) {
43 virtual ~MockImmersiveFullscreenControllerDelegate() {}
45 // ImmersiveFullscreenController::Delegate overrides:
46 virtual void OnImmersiveRevealStarted() OVERRIDE {
48 visible_fraction_ = 0;
50 virtual void OnImmersiveRevealEnded() OVERRIDE {
51 visible_fraction_ = 0;
53 virtual void OnImmersiveFullscreenExited() OVERRIDE {
55 visible_fraction_ = 1;
57 virtual void SetVisibleFraction(double visible_fraction) OVERRIDE {
58 visible_fraction_ = visible_fraction;
60 virtual std::vector<gfx::Rect> GetVisibleBoundsInScreen() const OVERRIDE {
61 std::vector<gfx::Rect> bounds_in_screen;
62 bounds_in_screen.push_back(top_container_view_->GetBoundsInScreen());
63 return bounds_in_screen;
66 bool is_enabled() const {
70 double visible_fraction() const {
71 return visible_fraction_;
75 views::View* top_container_view_;
77 double visible_fraction_;
79 DISALLOW_COPY_AND_ASSIGN(MockImmersiveFullscreenControllerDelegate);
82 class ConsumeEventHandler : public aura::test::TestEventHandler {
84 ConsumeEventHandler() {}
85 virtual ~ConsumeEventHandler() {}
88 virtual void OnEvent(ui::Event* event) OVERRIDE {
89 aura::test::TestEventHandler::OnEvent(event);
90 if (event->cancelable())
94 DISALLOW_COPY_AND_ASSIGN(ConsumeEventHandler);
99 /////////////////////////////////////////////////////////////////////////////
101 class ImmersiveFullscreenControllerTest : public ash::test::AshTestBase {
105 MODALITY_GESTURE_TAP,
106 MODALITY_GESTURE_SCROLL
109 ImmersiveFullscreenControllerTest()
111 top_container_(NULL),
112 content_view_(NULL) {}
113 virtual ~ImmersiveFullscreenControllerTest() {}
115 ImmersiveFullscreenController* controller() {
116 return controller_.get();
119 views::NativeViewHost* content_view() {
120 return content_view_;
123 views::View* top_container() {
124 return top_container_;
127 views::Widget* widget() { return widget_; }
129 aura::Window* window() {
130 return widget_->GetNativeWindow();
133 MockImmersiveFullscreenControllerDelegate* delegate() {
134 return delegate_.get();
137 // Access to private data from the controller.
138 bool top_edge_hover_timer_running() const {
139 return controller_->top_edge_hover_timer_.IsRunning();
141 int mouse_x_when_hit_top() const {
142 return controller_->mouse_x_when_hit_top_in_screen_;
145 // ash::test::AshTestBase overrides:
146 virtual void SetUp() OVERRIDE {
147 ash::test::AshTestBase::SetUp();
149 widget_ = new views::Widget();
150 views::Widget::InitParams params;
151 params.context = CurrentContext();
152 widget_->Init(params);
155 window()->SetProperty(aura::client::kShowStateKey,
156 ui::SHOW_STATE_FULLSCREEN);
158 gfx::Size window_size = widget_->GetWindowBoundsInScreen().size();
159 content_view_ = new views::NativeViewHost();
160 content_view_->SetBounds(0, 0, window_size.width(), window_size.height());
161 widget_->GetContentsView()->AddChildView(content_view_);
163 top_container_ = new views::View();
164 top_container_->SetBounds(
165 0, 0, window_size.width(), 100);
166 top_container_->SetFocusable(true);
167 widget_->GetContentsView()->AddChildView(top_container_);
170 new MockImmersiveFullscreenControllerDelegate(top_container_));
171 controller_.reset(new ImmersiveFullscreenController);
172 controller_->Init(delegate_.get(), widget_, top_container_);
173 controller_->SetupForTest();
175 // The mouse is moved so that it is not over |top_container_| by
179 // Enables / disables immersive fullscreen.
180 void SetEnabled(bool enabled) {
181 controller_->SetEnabled(ImmersiveFullscreenController::WINDOW_TYPE_OTHER,
185 // Attempt to reveal the top-of-window views via |modality|.
186 // The top-of-window views can only be revealed via mouse hover or a gesture.
187 void AttemptReveal(Modality modality) {
188 ASSERT_NE(modality, MODALITY_GESTURE_TAP);
189 AttemptRevealStateChange(true, modality);
192 // Attempt to unreveal the top-of-window views via |modality|. The
193 // top-of-window views can be unrevealed via any modality.
194 void AttemptUnreveal(Modality modality) {
195 AttemptRevealStateChange(false, modality);
198 // Sets whether the mouse is hovered above |top_container_|.
199 // SetHovered(true) moves the mouse over the |top_container_| but does not
200 // move it to the top of the screen so will not initiate a reveal.
201 void SetHovered(bool is_mouse_hovered) {
202 MoveMouse(0, is_mouse_hovered ? 10 : top_container_->height() + 100);
205 // Move the mouse to the given coordinates. The coordinates should be in
206 // |top_container_| coordinates.
207 void MoveMouse(int x, int y) {
208 gfx::Point screen_position(x, y);
209 views::View::ConvertPointToScreen(top_container_, &screen_position);
210 GetEventGenerator().MoveMouseTo(screen_position.x(), screen_position.y());
212 // If the top edge timer started running as a result of the mouse move, run
213 // the task which occurs after the timer delay. This reveals the
214 // top-of-window views synchronously if the mouse is hovered at the top of
216 if (controller()->top_edge_hover_timer_.IsRunning()) {
217 controller()->top_edge_hover_timer_.user_task().Run();
218 controller()->top_edge_hover_timer_.Stop();
223 // Attempt to change the revealed state to |revealed| via |modality|.
224 void AttemptRevealStateChange(bool revealed, Modality modality) {
225 // Compute the event position in |top_container_| coordinates.
226 gfx::Point event_position(0, revealed ? 0 : top_container_->height() + 100);
228 case MODALITY_MOUSE: {
229 MoveMouse(event_position.x(), event_position.y());
232 case MODALITY_GESTURE_TAP: {
233 gfx::Point screen_position = event_position;
234 views::View::ConvertPointToScreen(top_container_, &screen_position);
235 aura::test::EventGenerator& event_generator(GetEventGenerator());
236 event_generator.MoveTouch(event_position);
237 event_generator.PressTouch();
238 event_generator.ReleaseTouch();
241 case MODALITY_GESTURE_SCROLL: {
242 gfx::Point start(0, revealed ? 0 : top_container_->height() - 2);
243 gfx::Vector2d scroll_delta(0, 40);
244 gfx::Point end = revealed ? start + scroll_delta : start - scroll_delta;
245 views::View::ConvertPointToScreen(top_container_, &start);
246 views::View::ConvertPointToScreen(top_container_, &end);
247 aura::test::EventGenerator& event_generator(GetEventGenerator());
248 event_generator.GestureScrollSequence(
250 base::TimeDelta::FromMilliseconds(30), 1);
256 scoped_ptr<ImmersiveFullscreenController> controller_;
257 scoped_ptr<MockImmersiveFullscreenControllerDelegate> delegate_;
258 views::Widget* widget_; // Owned by the native widget.
259 views::View* top_container_; // Owned by |widget_|'s root-view.
260 views::NativeViewHost* content_view_; // Owned by |widget_|'s root-view.
262 DISALLOW_COPY_AND_ASSIGN(ImmersiveFullscreenControllerTest);
265 // Test the initial state and that the delegate gets notified of the
266 // top-of-window views getting hidden and revealed.
267 TEST_F(ImmersiveFullscreenControllerTest, Delegate) {
269 EXPECT_FALSE(controller()->IsEnabled());
270 EXPECT_FALSE(controller()->IsRevealed());
271 EXPECT_FALSE(delegate()->is_enabled());
273 // Enabling initially hides the top views.
275 EXPECT_TRUE(controller()->IsEnabled());
276 EXPECT_FALSE(controller()->IsRevealed());
277 EXPECT_TRUE(delegate()->is_enabled());
278 EXPECT_EQ(0, delegate()->visible_fraction());
280 // Revealing shows the top views.
281 AttemptReveal(MODALITY_MOUSE);
282 EXPECT_TRUE(controller()->IsEnabled());
283 EXPECT_TRUE(controller()->IsRevealed());
284 EXPECT_TRUE(delegate()->is_enabled());
285 EXPECT_EQ(1, delegate()->visible_fraction());
287 // Disabling ends the immersive reveal.
289 EXPECT_FALSE(controller()->IsEnabled());
290 EXPECT_FALSE(controller()->IsRevealed());
291 EXPECT_FALSE(delegate()->is_enabled());
294 // GetRevealedLock() specific tests.
295 TEST_F(ImmersiveFullscreenControllerTest, RevealedLock) {
296 scoped_ptr<ImmersiveRevealedLock> lock1;
297 scoped_ptr<ImmersiveRevealedLock> lock2;
299 // Immersive fullscreen is not on by default.
300 EXPECT_FALSE(controller()->IsEnabled());
302 // 1) Test acquiring and releasing a revealed state lock while immersive
303 // fullscreen is disabled. Acquiring or releasing the lock should have no
304 // effect till immersive fullscreen is enabled.
305 lock1.reset(controller()->GetRevealedLock(
306 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
307 EXPECT_FALSE(controller()->IsEnabled());
308 EXPECT_FALSE(controller()->IsRevealed());
310 // Immersive fullscreen should start in the revealed state due to the lock.
312 EXPECT_TRUE(controller()->IsEnabled());
313 EXPECT_TRUE(controller()->IsRevealed());
316 EXPECT_FALSE(controller()->IsEnabled());
317 EXPECT_FALSE(controller()->IsRevealed());
320 EXPECT_FALSE(controller()->IsEnabled());
321 EXPECT_FALSE(controller()->IsRevealed());
323 // Immersive fullscreen should start in the closed state because the lock is
326 EXPECT_TRUE(controller()->IsEnabled());
327 EXPECT_FALSE(controller()->IsRevealed());
329 // 2) Test that acquiring a lock reveals the top-of-window views if they are
331 lock1.reset(controller()->GetRevealedLock(
332 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
333 EXPECT_TRUE(controller()->IsRevealed());
335 // 3) Test that the top-of-window views are only hidden when all of the locks
337 lock2.reset(controller()->GetRevealedLock(
338 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
340 EXPECT_TRUE(controller()->IsRevealed());
343 EXPECT_FALSE(controller()->IsRevealed());
346 // Test mouse event processing for top-of-screen reveal triggering.
347 TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) {
348 // Set up initial state.
349 UpdateDisplay("800x600,800x600");
350 ash::DisplayLayout display_layout(ash::DisplayLayout::RIGHT, 0);
351 ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays(
354 // Set up initial state.
356 ASSERT_TRUE(controller()->IsEnabled());
357 ASSERT_FALSE(controller()->IsRevealed());
359 aura::test::EventGenerator& event_generator(GetEventGenerator());
361 gfx::Rect top_container_bounds_in_screen =
362 top_container()->GetBoundsInScreen();
363 // A position along the top edge of TopContainerView in screen coordinates.
364 gfx::Point top_edge_pos(top_container_bounds_in_screen.x() + 100,
365 top_container_bounds_in_screen.y());
367 // Mouse wheel event does nothing.
368 ui::MouseEvent wheel(
369 ui::ET_MOUSEWHEEL, top_edge_pos, top_edge_pos, ui::EF_NONE, ui::EF_NONE);
370 event_generator.Dispatch(&wheel);
371 EXPECT_FALSE(top_edge_hover_timer_running());
373 // Move to top edge of screen starts hover timer running. We cannot use
374 // MoveMouse() because MoveMouse() stops the timer if it started running.
375 event_generator.MoveMouseTo(top_edge_pos);
376 EXPECT_TRUE(top_edge_hover_timer_running());
377 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top());
379 // Moving |ImmersiveFullscreenControllerTest::kMouseRevealBoundsHeight| down
380 // from the top edge stops it.
381 event_generator.MoveMouseBy(0, 3);
382 EXPECT_FALSE(top_edge_hover_timer_running());
384 // Moving back to the top starts the timer again.
385 event_generator.MoveMouseTo(top_edge_pos);
386 EXPECT_TRUE(top_edge_hover_timer_running());
387 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top());
389 // Slight move to the right keeps the timer running for the same hit point.
390 event_generator.MoveMouseBy(1, 0);
391 EXPECT_TRUE(top_edge_hover_timer_running());
392 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top());
394 // Moving back to the left also keeps the timer running.
395 event_generator.MoveMouseBy(-1, 0);
396 EXPECT_TRUE(top_edge_hover_timer_running());
397 EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top());
399 // Large move right restarts the timer (so it is still running) and considers
400 // this a new hit at the top.
401 event_generator.MoveMouseTo(top_edge_pos.x() + 100, top_edge_pos.y());
402 EXPECT_TRUE(top_edge_hover_timer_running());
403 EXPECT_EQ(top_edge_pos.x() + 100, mouse_x_when_hit_top());
405 // Moving off the top edge horizontally stops the timer.
406 event_generator.MoveMouseTo(top_container_bounds_in_screen.right() + 1,
407 top_container_bounds_in_screen.y());
408 EXPECT_FALSE(top_edge_hover_timer_running());
410 // Once revealed, a move just a little below the top container doesn't end a
412 AttemptReveal(MODALITY_MOUSE);
413 event_generator.MoveMouseTo(top_container_bounds_in_screen.x(),
414 top_container_bounds_in_screen.bottom() + 1);
415 EXPECT_TRUE(controller()->IsRevealed());
417 // Once revealed, clicking just below the top container ends the reveal.
418 event_generator.ClickLeftButton();
419 EXPECT_FALSE(controller()->IsRevealed());
421 // Moving a lot below the top container ends a reveal.
422 AttemptReveal(MODALITY_MOUSE);
423 EXPECT_TRUE(controller()->IsRevealed());
424 event_generator.MoveMouseTo(top_container_bounds_in_screen.x(),
425 top_container_bounds_in_screen.bottom() + 50);
426 EXPECT_FALSE(controller()->IsRevealed());
428 // The mouse position cannot cause a reveal when the top container's widget
430 views::Widget* widget = top_container()->GetWidget();
431 widget->SetCapture(top_container());
432 AttemptReveal(MODALITY_MOUSE);
433 EXPECT_FALSE(controller()->IsRevealed());
434 widget->ReleaseCapture();
436 // The mouse position cannot end the reveal while the top container's widget
438 AttemptReveal(MODALITY_MOUSE);
439 EXPECT_TRUE(controller()->IsRevealed());
440 widget->SetCapture(top_container());
441 event_generator.MoveMouseTo(top_container_bounds_in_screen.x(),
442 top_container_bounds_in_screen.bottom() + 51);
443 EXPECT_TRUE(controller()->IsRevealed());
445 // Releasing capture should end the reveal.
446 widget->ReleaseCapture();
447 EXPECT_FALSE(controller()->IsRevealed());
450 // Test mouse event processing for top-of-screen reveal triggering when the
451 // top container's widget is inactive.
452 TEST_F(ImmersiveFullscreenControllerTest, Inactive) {
453 // Set up initial state.
454 views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds(
457 gfx::Rect(0, 0, 200, 200));
458 popup_widget->Show();
459 ASSERT_FALSE(top_container()->GetWidget()->IsActive());
462 ASSERT_TRUE(controller()->IsEnabled());
463 ASSERT_FALSE(controller()->IsRevealed());
465 gfx::Rect top_container_bounds_in_screen =
466 top_container()->GetBoundsInScreen();
467 gfx::Rect popup_bounds_in_screen = popup_widget->GetWindowBoundsInScreen();
468 ASSERT_EQ(top_container_bounds_in_screen.origin().ToString(),
469 popup_bounds_in_screen.origin().ToString());
470 ASSERT_GT(top_container_bounds_in_screen.right(),
471 popup_bounds_in_screen.right());
473 // The top-of-window views should stay hidden if the cursor is at the top edge
474 // but above an obscured portion of the top-of-window views.
475 MoveMouse(popup_bounds_in_screen.x(),
476 top_container_bounds_in_screen.y());
477 EXPECT_FALSE(controller()->IsRevealed());
479 // The top-of-window views should reveal if the cursor is at the top edge and
480 // above an unobscured portion of the top-of-window views.
481 MoveMouse(top_container_bounds_in_screen.right() - 1,
482 top_container_bounds_in_screen.y());
483 EXPECT_TRUE(controller()->IsRevealed());
485 // The top-of-window views should stay revealed if the cursor is moved off
487 MoveMouse(top_container_bounds_in_screen.right() - 1,
488 top_container_bounds_in_screen.bottom() - 1);
489 EXPECT_TRUE(controller()->IsRevealed());
491 // Moving way off of the top-of-window views should end the immersive reveal.
492 MoveMouse(top_container_bounds_in_screen.right() - 1,
493 top_container_bounds_in_screen.bottom() + 50);
494 EXPECT_FALSE(controller()->IsRevealed());
496 // Moving way off of the top-of-window views in a region where the
497 // top-of-window views are obscured should also end the immersive reveal.
498 // Ideally, the immersive reveal would end immediately when the cursor moves
499 // to an obscured portion of the top-of-window views.
500 MoveMouse(top_container_bounds_in_screen.right() - 1,
501 top_container_bounds_in_screen.y());
502 EXPECT_TRUE(controller()->IsRevealed());
503 MoveMouse(top_container_bounds_in_screen.x(),
504 top_container_bounds_in_screen.bottom() + 50);
505 EXPECT_FALSE(controller()->IsRevealed());
508 // Test mouse event processing for top-of-screen reveal triggering when the user
509 // has a vertical display layout (primary display above/below secondary display)
510 // and the immersive fullscreen window is on the bottom display.
511 TEST_F(ImmersiveFullscreenControllerTest, MouseEventsVerticalDisplayLayout) {
512 if (!SupportsMultipleDisplays())
515 // Set up initial state.
516 UpdateDisplay("800x600,800x600");
517 ash::DisplayLayout display_layout(ash::DisplayLayout::TOP, 0);
518 ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays(
522 ASSERT_TRUE(controller()->IsEnabled());
523 ASSERT_FALSE(controller()->IsRevealed());
525 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
526 ASSERT_EQ(root_windows[0],
527 top_container()->GetWidget()->GetNativeWindow()->GetRootWindow());
529 gfx::Rect primary_root_window_bounds_in_screen =
530 root_windows[0]->GetBoundsInScreen();
531 // Do not set |x| to the root window's x position because the display's
532 // corners have special behavior.
533 int x = primary_root_window_bounds_in_screen.x() + 10;
534 // The y position of the top edge of the primary display.
535 int y_top_edge = primary_root_window_bounds_in_screen.y();
537 aura::test::EventGenerator& event_generator(GetEventGenerator());
539 // Moving right below the top edge starts the hover timer running. We
540 // cannot use MoveMouse() because MoveMouse() stops the timer if it started
542 event_generator.MoveMouseTo(x, y_top_edge + 1);
543 EXPECT_TRUE(top_edge_hover_timer_running());
544 EXPECT_EQ(y_top_edge + 1,
545 aura::Env::GetInstance()->last_mouse_location().y());
547 // The timer should continue running if the user moves the mouse to the top
548 // edge even though the mouse is warped to the secondary display.
549 event_generator.MoveMouseTo(x, y_top_edge);
550 EXPECT_TRUE(top_edge_hover_timer_running());
551 EXPECT_NE(y_top_edge,
552 aura::Env::GetInstance()->last_mouse_location().y());
554 // The timer should continue running if the user overshoots the top edge
556 event_generator.MoveMouseTo(x, y_top_edge - 2);
557 EXPECT_TRUE(top_edge_hover_timer_running());
559 // The timer should stop running if the user overshoots the top edge by
561 event_generator.MoveMouseTo(x, y_top_edge - 20);
562 EXPECT_FALSE(top_edge_hover_timer_running());
564 // The timer should not start if the user moves the mouse to the bottom of the
565 // secondary display without crossing the top edge first.
566 event_generator.MoveMouseTo(x, y_top_edge - 2);
568 // Reveal the top-of-window views by overshooting the top edge slightly.
569 event_generator.MoveMouseTo(x, y_top_edge + 1);
570 // MoveMouse() runs the timer task.
571 MoveMouse(x, y_top_edge - 2);
572 EXPECT_TRUE(controller()->IsRevealed());
574 // The top-of-window views should stay revealed if the user moves the mouse
575 // around in the bottom region of the secondary display.
576 event_generator.MoveMouseTo(x + 10, y_top_edge - 3);
577 EXPECT_TRUE(controller()->IsRevealed());
579 // The top-of-window views should hide if the user moves the mouse away from
580 // the bottom region of the secondary display.
581 event_generator.MoveMouseTo(x, y_top_edge - 20);
582 EXPECT_FALSE(controller()->IsRevealed());
584 // Test that it is possible to reveal the top-of-window views by overshooting
585 // the top edge slightly when the top container's widget is not active.
586 views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds(
589 gfx::Rect(0, 200, 100, 100));
590 popup_widget->Show();
591 ASSERT_FALSE(top_container()->GetWidget()->IsActive());
592 ASSERT_FALSE(top_container()->GetBoundsInScreen().Intersects(
593 popup_widget->GetWindowBoundsInScreen()));
594 event_generator.MoveMouseTo(x, y_top_edge + 1);
595 MoveMouse(x, y_top_edge - 2);
596 EXPECT_TRUE(controller()->IsRevealed());
599 // Test behavior when the mouse becomes hovered without moving.
600 TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) {
602 scoped_ptr<ImmersiveRevealedLock> lock;
604 // 1) Test that if the mouse becomes hovered without the mouse moving due to a
605 // lock causing the top-of-window views to be revealed (and the mouse
606 // happening to be near the top of the screen), the top-of-window views do not
607 // hide till the mouse moves off of the top-of-window views.
609 EXPECT_FALSE(controller()->IsRevealed());
610 lock.reset(controller()->GetRevealedLock(
611 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
612 EXPECT_TRUE(controller()->IsRevealed());
614 EXPECT_TRUE(controller()->IsRevealed());
616 EXPECT_FALSE(controller()->IsRevealed());
618 // 2) Test that if the mouse becomes hovered without moving because of a
619 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there are no
620 // locks keeping the top-of-window views revealed, that mouse hover does not
621 // prevent the top-of-window views from closing.
624 EXPECT_FALSE(controller()->IsRevealed());
626 EXPECT_FALSE(controller()->IsRevealed());
628 // 3) Test that if the mouse becomes hovered without moving because of a
629 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there is a
630 // lock keeping the top-of-window views revealed, that the top-of-window views
631 // do not hide till the mouse moves off of the top-of-window views.
634 lock.reset(controller()->GetRevealedLock(
635 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
636 EXPECT_FALSE(controller()->IsRevealed());
638 EXPECT_TRUE(controller()->IsRevealed());
640 EXPECT_TRUE(controller()->IsRevealed());
642 EXPECT_FALSE(controller()->IsRevealed());
645 // Test revealing the top-of-window views using one modality and ending
646 // the reveal via another. For instance, initiating the reveal via a SWIPE_OPEN
647 // edge gesture, switching to using the mouse and ending the reveal by moving
648 // the mouse off of the top-of-window views.
649 TEST_F(ImmersiveFullscreenControllerTest, DifferentModalityEnterExit) {
651 EXPECT_TRUE(controller()->IsEnabled());
652 EXPECT_FALSE(controller()->IsRevealed());
654 // Initiate reveal via gesture, end reveal via mouse.
655 AttemptReveal(MODALITY_GESTURE_SCROLL);
656 EXPECT_TRUE(controller()->IsRevealed());
658 EXPECT_TRUE(controller()->IsRevealed());
659 AttemptUnreveal(MODALITY_MOUSE);
660 EXPECT_FALSE(controller()->IsRevealed());
662 // Initiate reveal via gesture, end reveal via touch.
663 AttemptReveal(MODALITY_GESTURE_SCROLL);
664 EXPECT_TRUE(controller()->IsRevealed());
665 AttemptUnreveal(MODALITY_GESTURE_TAP);
666 EXPECT_FALSE(controller()->IsRevealed());
668 // Initiate reveal via mouse, end reveal via gesture.
669 AttemptReveal(MODALITY_MOUSE);
670 EXPECT_TRUE(controller()->IsRevealed());
671 AttemptUnreveal(MODALITY_GESTURE_SCROLL);
672 EXPECT_FALSE(controller()->IsRevealed());
674 // Initiate reveal via mouse, end reveal via touch.
675 AttemptReveal(MODALITY_MOUSE);
676 EXPECT_TRUE(controller()->IsRevealed());
677 AttemptUnreveal(MODALITY_GESTURE_TAP);
678 EXPECT_FALSE(controller()->IsRevealed());
681 // Test when the SWIPE_CLOSE edge gesture closes the top-of-window views.
682 TEST_F(ImmersiveFullscreenControllerTest, EndRevealViaGesture) {
684 EXPECT_TRUE(controller()->IsEnabled());
685 EXPECT_FALSE(controller()->IsRevealed());
687 // A gesture should be able to close the top-of-window views when
688 // top-of-window views have focus.
689 AttemptReveal(MODALITY_MOUSE);
690 top_container()->RequestFocus();
691 EXPECT_TRUE(controller()->IsRevealed());
692 AttemptUnreveal(MODALITY_GESTURE_SCROLL);
693 EXPECT_FALSE(controller()->IsRevealed());
695 // The top-of-window views should no longer have focus. Clearing focus is
696 // important because it closes focus-related popup windows like the touch
697 // selection handles.
698 EXPECT_FALSE(top_container()->HasFocus());
700 // If some other code is holding onto a lock, a gesture should not be able to
702 AttemptReveal(MODALITY_MOUSE);
703 scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock(
704 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
705 EXPECT_TRUE(controller()->IsRevealed());
706 AttemptUnreveal(MODALITY_GESTURE_SCROLL);
707 EXPECT_TRUE(controller()->IsRevealed());
709 EXPECT_FALSE(controller()->IsRevealed());
712 // Tests that touch-gesture can be used to reveal the top-of-window views when
713 // the child window consumes all events.
714 TEST_F(ImmersiveFullscreenControllerTest, RevealViaGestureChildConsumesEvents) {
715 // Enabling initially hides the top views.
717 EXPECT_TRUE(controller()->IsEnabled());
718 EXPECT_FALSE(controller()->IsRevealed());
720 aura::test::TestWindowDelegate child_delegate;
721 scoped_ptr<aura::Window> child(
722 CreateTestWindowInShellWithDelegateAndType(&child_delegate,
723 ui::wm::WINDOW_TYPE_CONTROL,
726 content_view()->Attach(child.get());
729 ConsumeEventHandler handler;
730 child->AddPreTargetHandler(&handler);
732 // Reveal the top views using a touch-scroll gesture. The child window should
733 // not receive the touch events.
734 AttemptReveal(MODALITY_GESTURE_SCROLL);
735 EXPECT_TRUE(controller()->IsRevealed());
736 EXPECT_EQ(0, handler.num_touch_events());
738 AttemptUnreveal(MODALITY_GESTURE_TAP);
739 EXPECT_FALSE(controller()->IsRevealed());
740 EXPECT_GT(handler.num_touch_events(), 0);
741 child->RemovePreTargetHandler(&handler);
744 // Make sure touch events towards the top of the window do not leak through to
745 // windows underneath.
746 TEST_F(ImmersiveFullscreenControllerTest, EventsDoNotLeakToWindowUnderneath) {
747 gfx::Rect window_bounds = window()->GetBoundsInScreen();
748 aura::test::TestWindowDelegate child_delegate;
749 scoped_ptr<aura::Window> behind(CreateTestWindowInShellWithDelegate(
750 &child_delegate, 1234, window_bounds));
752 behind->SetBounds(window_bounds);
753 widget()->StackAbove(behind.get());
755 // Make sure the windows are aligned on top.
756 EXPECT_EQ(behind->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
757 int top = behind->GetBoundsInScreen().y();
759 ui::TouchEvent touch(ui::ET_TOUCH_MOVED, gfx::Point(10, top), 0,
760 ui::EventTimeForNow());
761 ui::EventTarget* root = window()->GetRootWindow();
762 ui::EventTargeter* targeter = root->GetEventTargeter();
763 EXPECT_EQ(window(), targeter->FindTargetForEvent(root, &touch));
766 EXPECT_FALSE(controller()->IsRevealed());
767 // Make sure the windows are still aligned on top.
768 EXPECT_EQ(behind->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
769 top = behind->GetBoundsInScreen().y();
770 ui::TouchEvent touch2(ui::ET_TOUCH_MOVED, gfx::Point(10, top), 0,
771 ui::EventTimeForNow());
772 // The event should still be targeted to window().
773 EXPECT_EQ(window(), targeter->FindTargetForEvent(root, &touch2));
776 // Do not test under windows because focus testing is not reliable on
777 // Windows. (crbug.com/79493)
780 // Test how focus and activation affects whether the top-of-window views are
782 TEST_F(ImmersiveFullscreenControllerTest, Focus) {
783 // Add views to the view hierarchy which we will focus and unfocus during the
785 views::View* child_view = new views::View();
786 child_view->SetBounds(0, 0, 10, 10);
787 child_view->SetFocusable(true);
788 top_container()->AddChildView(child_view);
789 views::View* unrelated_view = new views::View();
790 unrelated_view->SetBounds(0, 100, 10, 10);
791 unrelated_view->SetFocusable(true);
792 top_container()->parent()->AddChildView(unrelated_view);
793 views::FocusManager* focus_manager =
794 top_container()->GetWidget()->GetFocusManager();
798 // 1) Test that the top-of-window views stay revealed as long as either a
799 // |child_view| has focus or the mouse is hovered above the top-of-window
801 AttemptReveal(MODALITY_MOUSE);
802 child_view->RequestFocus();
803 focus_manager->ClearFocus();
804 EXPECT_TRUE(controller()->IsRevealed());
805 child_view->RequestFocus();
807 EXPECT_TRUE(controller()->IsRevealed());
808 focus_manager->ClearFocus();
809 EXPECT_FALSE(controller()->IsRevealed());
811 // 2) Test that focusing |unrelated_view| hides the top-of-window views.
812 // Note: In this test we can cheat and trigger a reveal via focus because
813 // the top container does not hide when the top-of-window views are not
815 child_view->RequestFocus();
816 EXPECT_TRUE(controller()->IsRevealed());
817 unrelated_view->RequestFocus();
818 EXPECT_FALSE(controller()->IsRevealed());
820 // 3) Test that a loss of focus of |child_view| to |unrelated_view|
821 // while immersive mode is disabled is properly registered.
822 child_view->RequestFocus();
823 EXPECT_TRUE(controller()->IsRevealed());
825 EXPECT_FALSE(controller()->IsRevealed());
826 unrelated_view->RequestFocus();
828 EXPECT_FALSE(controller()->IsRevealed());
830 // Repeat test but with a revealed lock acquired when immersive mode is
831 // disabled because the code path is different.
832 child_view->RequestFocus();
833 EXPECT_TRUE(controller()->IsRevealed());
835 scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock(
836 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
837 EXPECT_FALSE(controller()->IsRevealed());
838 unrelated_view->RequestFocus();
840 EXPECT_TRUE(controller()->IsRevealed());
842 EXPECT_FALSE(controller()->IsRevealed());
845 // Test how transient windows affect whether the top-of-window views are
847 TEST_F(ImmersiveFullscreenControllerTest, Transient) {
848 views::Widget* top_container_widget = top_container()->GetWidget();
851 ASSERT_FALSE(controller()->IsRevealed());
853 // 1) Test that a transient window which is not a bubble does not trigger a
854 // reveal but does keep the top-of-window views revealed if they are already
856 views::Widget::InitParams transient_params;
857 transient_params.ownership =
858 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
859 transient_params.parent = top_container_widget->GetNativeView();
860 transient_params.bounds = gfx::Rect(0, 100, 100, 100);
861 scoped_ptr<views::Widget> transient_widget(new views::Widget());
862 transient_widget->Init(transient_params);
864 EXPECT_FALSE(controller()->IsRevealed());
865 AttemptReveal(MODALITY_MOUSE);
866 EXPECT_TRUE(controller()->IsRevealed());
867 transient_widget->Show();
869 EXPECT_TRUE(controller()->IsRevealed());
870 transient_widget.reset();
871 EXPECT_FALSE(controller()->IsRevealed());
873 // 2) Test that activating a non-transient window does not keep the
874 // top-of-window views revealed.
875 views::Widget::InitParams non_transient_params;
876 non_transient_params.ownership =
877 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
878 non_transient_params.context = top_container_widget->GetNativeView();
879 non_transient_params.bounds = gfx::Rect(0, 100, 100, 100);
880 scoped_ptr<views::Widget> non_transient_widget(new views::Widget());
881 non_transient_widget->Init(non_transient_params);
883 EXPECT_FALSE(controller()->IsRevealed());
884 AttemptReveal(MODALITY_MOUSE);
885 EXPECT_TRUE(controller()->IsRevealed());
886 non_transient_widget->Show();
888 EXPECT_FALSE(controller()->IsRevealed());
891 // Test how bubbles affect whether the top-of-window views are revealed.
892 TEST_F(ImmersiveFullscreenControllerTest, Bubbles) {
893 scoped_ptr<ImmersiveRevealedLock> revealed_lock;
894 views::Widget* top_container_widget = top_container()->GetWidget();
896 // Add views to the view hierarchy to which we will anchor bubbles.
897 views::View* child_view = new views::View();
898 child_view->SetBounds(0, 0, 10, 10);
899 top_container()->AddChildView(child_view);
900 views::View* unrelated_view = new views::View();
901 unrelated_view->SetBounds(0, 100, 10, 10);
902 top_container()->parent()->AddChildView(unrelated_view);
905 ASSERT_FALSE(controller()->IsRevealed());
907 // 1) Test that a bubble anchored to a child of the top container triggers
908 // a reveal and keeps the top-of-window views revealed for the duration of
910 views::Widget* bubble_widget1(views::BubbleDelegateView::CreateBubble(
911 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)));
912 bubble_widget1->Show();
913 EXPECT_TRUE(controller()->IsRevealed());
915 // Activating |top_container_widget| will close |bubble_widget1|.
916 top_container_widget->Activate();
917 AttemptReveal(MODALITY_MOUSE);
918 revealed_lock.reset(controller()->GetRevealedLock(
919 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
920 EXPECT_TRUE(controller()->IsRevealed());
922 views::Widget* bubble_widget2 = views::BubbleDelegateView::CreateBubble(
923 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
924 bubble_widget2->Show();
925 EXPECT_TRUE(controller()->IsRevealed());
926 revealed_lock.reset();
928 EXPECT_TRUE(controller()->IsRevealed());
929 bubble_widget2->Close();
930 EXPECT_FALSE(controller()->IsRevealed());
932 // 2) Test that transitioning from keeping the top-of-window views revealed
933 // because of a bubble to keeping the top-of-window views revealed because of
934 // mouse hover by activating |top_container_widget| works.
935 views::Widget* bubble_widget3 = views::BubbleDelegateView::CreateBubble(
936 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
937 bubble_widget3->Show();
939 EXPECT_TRUE(controller()->IsRevealed());
940 top_container_widget->Activate();
941 EXPECT_TRUE(controller()->IsRevealed());
943 // 3) Test that the top-of-window views stay revealed as long as at least one
944 // bubble anchored to a child of the top container is visible.
946 EXPECT_FALSE(controller()->IsRevealed());
948 views::BubbleDelegateView* bubble_delegate4(new views::BubbleDelegateView(
949 child_view, views::BubbleBorder::NONE));
950 bubble_delegate4->set_use_focusless(true);
951 views::Widget* bubble_widget4(views::BubbleDelegateView::CreateBubble(
953 bubble_widget4->Show();
955 views::BubbleDelegateView* bubble_delegate5(new views::BubbleDelegateView(
956 child_view, views::BubbleBorder::NONE));
957 bubble_delegate5->set_use_focusless(true);
958 views::Widget* bubble_widget5(views::BubbleDelegateView::CreateBubble(
960 bubble_widget5->Show();
962 EXPECT_TRUE(controller()->IsRevealed());
963 bubble_widget4->Hide();
964 EXPECT_TRUE(controller()->IsRevealed());
965 bubble_widget5->Hide();
966 EXPECT_FALSE(controller()->IsRevealed());
967 bubble_widget5->Show();
968 EXPECT_TRUE(controller()->IsRevealed());
970 // 4) Test that visibility changes which occur while immersive fullscreen is
971 // disabled are handled upon reenabling immersive fullscreen.
973 bubble_widget5->Hide();
975 EXPECT_FALSE(controller()->IsRevealed());
977 // We do not need |bubble_widget4| or |bubble_widget5| anymore, close them.
978 bubble_widget4->Close();
979 bubble_widget5->Close();
981 // 5) Test that a bubble added while immersive fullscreen is disabled is
982 // handled upon reenabling immersive fullscreen.
985 views::Widget* bubble_widget6 = views::BubbleDelegateView::CreateBubble(
986 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
987 bubble_widget6->Show();
990 EXPECT_TRUE(controller()->IsRevealed());
992 bubble_widget6->Close();
994 // 6) Test that a bubble which is not anchored to a child of the
995 // TopContainerView does not trigger a reveal or keep the
996 // top-of-window views revealed if they are already revealed.
997 views::Widget* bubble_widget7 = views::BubbleDelegateView::CreateBubble(
998 new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE));
999 bubble_widget7->Show();
1000 EXPECT_FALSE(controller()->IsRevealed());
1002 // Activating |top_container_widget| will close |bubble_widget6|.
1003 top_container_widget->Activate();
1004 AttemptReveal(MODALITY_MOUSE);
1005 EXPECT_TRUE(controller()->IsRevealed());
1007 views::Widget* bubble_widget8 = views::BubbleDelegateView::CreateBubble(
1008 new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE));
1009 bubble_widget8->Show();
1011 EXPECT_FALSE(controller()->IsRevealed());
1012 bubble_widget8->Close();
1015 #endif // defined(OS_WIN)
1017 // Test that the shelf is set to auto hide as long as the window is in
1018 // immersive fullscreen and that the shelf's state before entering immersive
1019 // fullscreen is restored upon exiting immersive fullscreen.
1020 TEST_F(ImmersiveFullscreenControllerTest, Shelf) {
1021 ash::internal::ShelfLayoutManager* shelf =
1022 ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
1024 // Shelf is visible by default.
1025 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1026 ASSERT_FALSE(controller()->IsEnabled());
1027 ASSERT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state());
1029 // Entering immersive fullscreen sets the shelf to auto hide.
1030 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
1032 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1034 // Disabling immersive fullscreen puts it back.
1036 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1037 ASSERT_FALSE(controller()->IsEnabled());
1038 EXPECT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state());
1040 // The user could toggle the shelf auto-hide behavior.
1041 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
1042 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1044 // Entering immersive fullscreen keeps auto-hide.
1045 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
1047 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1049 // Disabling immersive fullscreen maintains the user's auto-hide selection.
1051 window()->SetProperty(aura::client::kShowStateKey,
1052 ui::SHOW_STATE_NORMAL);
1053 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1058 #endif // defined(OS_CHROMEOS)