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/test/event_generator.h"
17 #include "ui/aura/test/test_window_delegate.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/window_event_dispatcher.h"
20 #include "ui/events/event_utils.h"
21 #include "ui/events/test/test_event_handler.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"
32 class MockImmersiveFullscreenControllerDelegate
33 : public ImmersiveFullscreenController::Delegate {
35 MockImmersiveFullscreenControllerDelegate(views::View* top_container_view)
36 : top_container_view_(top_container_view),
38 visible_fraction_(1) {
40 virtual ~MockImmersiveFullscreenControllerDelegate() {}
42 // ImmersiveFullscreenController::Delegate overrides:
43 virtual void OnImmersiveRevealStarted() OVERRIDE {
45 visible_fraction_ = 0;
47 virtual void OnImmersiveRevealEnded() OVERRIDE {
48 visible_fraction_ = 0;
50 virtual void OnImmersiveFullscreenExited() OVERRIDE {
52 visible_fraction_ = 1;
54 virtual void SetVisibleFraction(double visible_fraction) OVERRIDE {
55 visible_fraction_ = visible_fraction;
57 virtual 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 virtual ~ConsumeEventHandler() {}
85 virtual 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 virtual ~ImmersiveFullscreenControllerTest() {}
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 virtual 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 aura::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 aura::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 aura::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(
360 ui::ET_MOUSEWHEEL, top_edge_pos, top_edge_pos, 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 aura::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());
543 EXPECT_NE(y_top_edge,
544 aura::Env::GetInstance()->last_mouse_location().y());
546 // The timer should continue running if the user overshoots the top edge
548 event_generator.MoveMouseTo(x, y_top_edge - 2);
549 EXPECT_TRUE(top_edge_hover_timer_running());
551 // The timer should stop running if the user overshoots the top edge by
553 event_generator.MoveMouseTo(x, y_top_edge - 20);
554 EXPECT_FALSE(top_edge_hover_timer_running());
556 // The timer should not start if the user moves the mouse to the bottom of the
557 // secondary display without crossing the top edge first.
558 event_generator.MoveMouseTo(x, y_top_edge - 2);
560 // Reveal the top-of-window views by overshooting the top edge slightly.
561 event_generator.MoveMouseTo(x, y_top_edge + 1);
562 // MoveMouse() runs the timer task.
563 MoveMouse(x, y_top_edge - 2);
564 EXPECT_TRUE(controller()->IsRevealed());
566 // The top-of-window views should stay revealed if the user moves the mouse
567 // around in the bottom region of the secondary display.
568 event_generator.MoveMouseTo(x + 10, y_top_edge - 3);
569 EXPECT_TRUE(controller()->IsRevealed());
571 // The top-of-window views should hide if the user moves the mouse away from
572 // the bottom region of the secondary display.
573 event_generator.MoveMouseTo(x, y_top_edge - 20);
574 EXPECT_FALSE(controller()->IsRevealed());
576 // Test that it is possible to reveal the top-of-window views by overshooting
577 // the top edge slightly when the top container's widget is not active.
578 views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds(
581 gfx::Rect(0, 200, 100, 100));
582 popup_widget->Show();
583 ASSERT_FALSE(top_container()->GetWidget()->IsActive());
584 ASSERT_FALSE(top_container()->GetBoundsInScreen().Intersects(
585 popup_widget->GetWindowBoundsInScreen()));
586 event_generator.MoveMouseTo(x, y_top_edge + 1);
587 MoveMouse(x, y_top_edge - 2);
588 EXPECT_TRUE(controller()->IsRevealed());
591 // Test behavior when the mouse becomes hovered without moving.
592 TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) {
594 scoped_ptr<ImmersiveRevealedLock> lock;
596 // 1) Test that if the mouse becomes hovered without the mouse moving due to a
597 // lock causing the top-of-window views to be revealed (and the mouse
598 // happening to be near the top of the screen), the top-of-window views do not
599 // hide till the mouse moves off of the top-of-window views.
601 EXPECT_FALSE(controller()->IsRevealed());
602 lock.reset(controller()->GetRevealedLock(
603 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
604 EXPECT_TRUE(controller()->IsRevealed());
606 EXPECT_TRUE(controller()->IsRevealed());
608 EXPECT_FALSE(controller()->IsRevealed());
610 // 2) Test that if the mouse becomes hovered without moving because of a
611 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there are no
612 // locks keeping the top-of-window views revealed, that mouse hover does not
613 // prevent the top-of-window views from closing.
616 EXPECT_FALSE(controller()->IsRevealed());
618 EXPECT_FALSE(controller()->IsRevealed());
620 // 3) Test that if the mouse becomes hovered without moving because of a
621 // reveal in ImmersiveFullscreenController::SetEnabled(true) and there is a
622 // lock keeping the top-of-window views revealed, that the top-of-window views
623 // do not hide till the mouse moves off of the top-of-window views.
626 lock.reset(controller()->GetRevealedLock(
627 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
628 EXPECT_FALSE(controller()->IsRevealed());
630 EXPECT_TRUE(controller()->IsRevealed());
632 EXPECT_TRUE(controller()->IsRevealed());
634 EXPECT_FALSE(controller()->IsRevealed());
637 // Test revealing the top-of-window views using one modality and ending
638 // the reveal via another. For instance, initiating the reveal via a SWIPE_OPEN
639 // edge gesture, switching to using the mouse and ending the reveal by moving
640 // the mouse off of the top-of-window views.
641 TEST_F(ImmersiveFullscreenControllerTest, DifferentModalityEnterExit) {
643 EXPECT_TRUE(controller()->IsEnabled());
644 EXPECT_FALSE(controller()->IsRevealed());
646 // Initiate reveal via gesture, end reveal via mouse.
647 AttemptReveal(MODALITY_GESTURE_SCROLL);
648 EXPECT_TRUE(controller()->IsRevealed());
650 EXPECT_TRUE(controller()->IsRevealed());
651 AttemptUnreveal(MODALITY_MOUSE);
652 EXPECT_FALSE(controller()->IsRevealed());
654 // Initiate reveal via gesture, end reveal via touch.
655 AttemptReveal(MODALITY_GESTURE_SCROLL);
656 EXPECT_TRUE(controller()->IsRevealed());
657 AttemptUnreveal(MODALITY_GESTURE_TAP);
658 EXPECT_FALSE(controller()->IsRevealed());
660 // Initiate reveal via mouse, end reveal via gesture.
661 AttemptReveal(MODALITY_MOUSE);
662 EXPECT_TRUE(controller()->IsRevealed());
663 AttemptUnreveal(MODALITY_GESTURE_SCROLL);
664 EXPECT_FALSE(controller()->IsRevealed());
666 // Initiate reveal via mouse, end reveal via touch.
667 AttemptReveal(MODALITY_MOUSE);
668 EXPECT_TRUE(controller()->IsRevealed());
669 AttemptUnreveal(MODALITY_GESTURE_TAP);
670 EXPECT_FALSE(controller()->IsRevealed());
673 // Test when the SWIPE_CLOSE edge gesture closes the top-of-window views.
675 // On Windows, touch events do not result in mouse events being disabled. As
676 // a result, the last part of this test which ends the reveal via a gesture will
677 // not work correctly. See crbug.com/332430, and the function
678 // ShouldHideCursorOnTouch() in compound_event_filter.cc.
679 #define MAYBE_EndRevealViaGesture DISABLED_EndRevealViaGesture
681 #define MAYBE_EndRevealViaGesture EndRevealViaGesture
683 TEST_F(ImmersiveFullscreenControllerTest, MAYBE_EndRevealViaGesture) {
685 EXPECT_TRUE(controller()->IsEnabled());
686 EXPECT_FALSE(controller()->IsRevealed());
688 // A gesture should be able to close the top-of-window views when
689 // top-of-window views have focus.
690 AttemptReveal(MODALITY_MOUSE);
691 top_container()->RequestFocus();
692 EXPECT_TRUE(controller()->IsRevealed());
693 AttemptUnreveal(MODALITY_GESTURE_SCROLL);
694 EXPECT_FALSE(controller()->IsRevealed());
696 // The top-of-window views should no longer have focus. Clearing focus is
697 // important because it closes focus-related popup windows like the touch
698 // selection handles.
699 EXPECT_FALSE(top_container()->HasFocus());
701 // If some other code is holding onto a lock, a gesture should not be able to
703 AttemptReveal(MODALITY_MOUSE);
704 scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock(
705 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
706 EXPECT_TRUE(controller()->IsRevealed());
707 AttemptUnreveal(MODALITY_GESTURE_SCROLL);
708 EXPECT_TRUE(controller()->IsRevealed());
710 EXPECT_FALSE(controller()->IsRevealed());
713 // Tests that touch-gesture can be used to reveal the top-of-window views when
714 // the child window consumes all events.
715 TEST_F(ImmersiveFullscreenControllerTest, RevealViaGestureChildConsumesEvents) {
716 // Enabling initially hides the top views.
718 EXPECT_TRUE(controller()->IsEnabled());
719 EXPECT_FALSE(controller()->IsRevealed());
721 aura::test::TestWindowDelegate child_delegate;
722 scoped_ptr<aura::Window> child(
723 CreateTestWindowInShellWithDelegateAndType(&child_delegate,
724 ui::wm::WINDOW_TYPE_CONTROL,
727 content_view()->Attach(child.get());
730 ConsumeEventHandler handler;
731 child->AddPreTargetHandler(&handler);
733 // Reveal the top views using a touch-scroll gesture. The child window should
734 // not receive the touch events.
735 AttemptReveal(MODALITY_GESTURE_SCROLL);
736 EXPECT_TRUE(controller()->IsRevealed());
737 EXPECT_EQ(0, handler.num_touch_events());
739 AttemptUnreveal(MODALITY_GESTURE_TAP);
740 EXPECT_FALSE(controller()->IsRevealed());
741 EXPECT_GT(handler.num_touch_events(), 0);
742 child->RemovePreTargetHandler(&handler);
745 // Make sure touch events towards the top of the window do not leak through to
746 // windows underneath.
747 TEST_F(ImmersiveFullscreenControllerTest, EventsDoNotLeakToWindowUnderneath) {
748 gfx::Rect window_bounds = window()->GetBoundsInScreen();
749 aura::test::TestWindowDelegate child_delegate;
750 scoped_ptr<aura::Window> behind(CreateTestWindowInShellWithDelegate(
751 &child_delegate, 1234, window_bounds));
753 behind->SetBounds(window_bounds);
754 widget()->StackAbove(behind.get());
756 // Make sure the windows are aligned on top.
757 EXPECT_EQ(behind->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
758 int top = behind->GetBoundsInScreen().y();
760 ui::TouchEvent touch(ui::ET_TOUCH_MOVED, gfx::Point(10, top), 0,
761 ui::EventTimeForNow());
762 ui::EventTarget* root = window()->GetRootWindow();
763 ui::EventTargeter* targeter = root->GetEventTargeter();
764 EXPECT_EQ(window(), targeter->FindTargetForEvent(root, &touch));
767 EXPECT_FALSE(controller()->IsRevealed());
768 // Make sure the windows are still aligned on top.
769 EXPECT_EQ(behind->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
770 top = behind->GetBoundsInScreen().y();
771 ui::TouchEvent touch2(ui::ET_TOUCH_MOVED, gfx::Point(10, top), 0,
772 ui::EventTimeForNow());
773 // The event should still be targeted to window().
774 EXPECT_EQ(window(), targeter->FindTargetForEvent(root, &touch2));
777 // Do not test under windows because focus testing is not reliable on
778 // Windows. (crbug.com/79493)
781 // Test how focus and activation affects whether the top-of-window views are
783 TEST_F(ImmersiveFullscreenControllerTest, Focus) {
784 // Add views to the view hierarchy which we will focus and unfocus during the
786 views::View* child_view = new views::View();
787 child_view->SetBounds(0, 0, 10, 10);
788 child_view->SetFocusable(true);
789 top_container()->AddChildView(child_view);
790 views::View* unrelated_view = new views::View();
791 unrelated_view->SetBounds(0, 100, 10, 10);
792 unrelated_view->SetFocusable(true);
793 top_container()->parent()->AddChildView(unrelated_view);
794 views::FocusManager* focus_manager =
795 top_container()->GetWidget()->GetFocusManager();
799 // 1) Test that the top-of-window views stay revealed as long as either a
800 // |child_view| has focus or the mouse is hovered above the top-of-window
802 AttemptReveal(MODALITY_MOUSE);
803 child_view->RequestFocus();
804 focus_manager->ClearFocus();
805 EXPECT_TRUE(controller()->IsRevealed());
806 child_view->RequestFocus();
808 EXPECT_TRUE(controller()->IsRevealed());
809 focus_manager->ClearFocus();
810 EXPECT_FALSE(controller()->IsRevealed());
812 // 2) Test that focusing |unrelated_view| hides the top-of-window views.
813 // Note: In this test we can cheat and trigger a reveal via focus because
814 // the top container does not hide when the top-of-window views are not
816 child_view->RequestFocus();
817 EXPECT_TRUE(controller()->IsRevealed());
818 unrelated_view->RequestFocus();
819 EXPECT_FALSE(controller()->IsRevealed());
821 // 3) Test that a loss of focus of |child_view| to |unrelated_view|
822 // while immersive mode is disabled is properly registered.
823 child_view->RequestFocus();
824 EXPECT_TRUE(controller()->IsRevealed());
826 EXPECT_FALSE(controller()->IsRevealed());
827 unrelated_view->RequestFocus();
829 EXPECT_FALSE(controller()->IsRevealed());
831 // Repeat test but with a revealed lock acquired when immersive mode is
832 // disabled because the code path is different.
833 child_view->RequestFocus();
834 EXPECT_TRUE(controller()->IsRevealed());
836 scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock(
837 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
838 EXPECT_FALSE(controller()->IsRevealed());
839 unrelated_view->RequestFocus();
841 EXPECT_TRUE(controller()->IsRevealed());
843 EXPECT_FALSE(controller()->IsRevealed());
846 // Test how transient windows affect whether the top-of-window views are
848 TEST_F(ImmersiveFullscreenControllerTest, Transient) {
849 views::Widget* top_container_widget = top_container()->GetWidget();
852 ASSERT_FALSE(controller()->IsRevealed());
854 // 1) Test that a transient window which is not a bubble does not trigger a
855 // reveal but does keep the top-of-window views revealed if they are already
857 views::Widget::InitParams transient_params;
858 transient_params.ownership =
859 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
860 transient_params.parent = top_container_widget->GetNativeView();
861 transient_params.bounds = gfx::Rect(0, 100, 100, 100);
862 scoped_ptr<views::Widget> transient_widget(new views::Widget());
863 transient_widget->Init(transient_params);
865 EXPECT_FALSE(controller()->IsRevealed());
866 AttemptReveal(MODALITY_MOUSE);
867 EXPECT_TRUE(controller()->IsRevealed());
868 transient_widget->Show();
870 EXPECT_TRUE(controller()->IsRevealed());
871 transient_widget.reset();
872 EXPECT_FALSE(controller()->IsRevealed());
874 // 2) Test that activating a non-transient window does not keep the
875 // top-of-window views revealed.
876 views::Widget::InitParams non_transient_params;
877 non_transient_params.ownership =
878 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
879 non_transient_params.context = top_container_widget->GetNativeView();
880 non_transient_params.bounds = gfx::Rect(0, 100, 100, 100);
881 scoped_ptr<views::Widget> non_transient_widget(new views::Widget());
882 non_transient_widget->Init(non_transient_params);
884 EXPECT_FALSE(controller()->IsRevealed());
885 AttemptReveal(MODALITY_MOUSE);
886 EXPECT_TRUE(controller()->IsRevealed());
887 non_transient_widget->Show();
889 EXPECT_FALSE(controller()->IsRevealed());
892 // Test how bubbles affect whether the top-of-window views are revealed.
893 TEST_F(ImmersiveFullscreenControllerTest, Bubbles) {
894 scoped_ptr<ImmersiveRevealedLock> revealed_lock;
895 views::Widget* top_container_widget = top_container()->GetWidget();
897 // Add views to the view hierarchy to which we will anchor bubbles.
898 views::View* child_view = new views::View();
899 child_view->SetBounds(0, 0, 10, 10);
900 top_container()->AddChildView(child_view);
901 views::View* unrelated_view = new views::View();
902 unrelated_view->SetBounds(0, 100, 10, 10);
903 top_container()->parent()->AddChildView(unrelated_view);
906 ASSERT_FALSE(controller()->IsRevealed());
908 // 1) Test that a bubble anchored to a child of the top container triggers
909 // a reveal and keeps the top-of-window views revealed for the duration of
911 views::Widget* bubble_widget1(views::BubbleDelegateView::CreateBubble(
912 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)));
913 bubble_widget1->Show();
914 EXPECT_TRUE(controller()->IsRevealed());
916 // Activating |top_container_widget| will close |bubble_widget1|.
917 top_container_widget->Activate();
918 AttemptReveal(MODALITY_MOUSE);
919 revealed_lock.reset(controller()->GetRevealedLock(
920 ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
921 EXPECT_TRUE(controller()->IsRevealed());
923 views::Widget* bubble_widget2 = views::BubbleDelegateView::CreateBubble(
924 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
925 bubble_widget2->Show();
926 EXPECT_TRUE(controller()->IsRevealed());
927 revealed_lock.reset();
929 EXPECT_TRUE(controller()->IsRevealed());
930 bubble_widget2->Close();
931 EXPECT_FALSE(controller()->IsRevealed());
933 // 2) Test that transitioning from keeping the top-of-window views revealed
934 // because of a bubble to keeping the top-of-window views revealed because of
935 // mouse hover by activating |top_container_widget| works.
936 views::Widget* bubble_widget3 = views::BubbleDelegateView::CreateBubble(
937 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
938 bubble_widget3->Show();
940 EXPECT_TRUE(controller()->IsRevealed());
941 top_container_widget->Activate();
942 EXPECT_TRUE(controller()->IsRevealed());
944 // 3) Test that the top-of-window views stay revealed as long as at least one
945 // bubble anchored to a child of the top container is visible.
947 EXPECT_FALSE(controller()->IsRevealed());
949 views::BubbleDelegateView* bubble_delegate4(new views::BubbleDelegateView(
950 child_view, views::BubbleBorder::NONE));
951 bubble_delegate4->set_use_focusless(true);
952 views::Widget* bubble_widget4(views::BubbleDelegateView::CreateBubble(
954 bubble_widget4->Show();
956 views::BubbleDelegateView* bubble_delegate5(new views::BubbleDelegateView(
957 child_view, views::BubbleBorder::NONE));
958 bubble_delegate5->set_use_focusless(true);
959 views::Widget* bubble_widget5(views::BubbleDelegateView::CreateBubble(
961 bubble_widget5->Show();
963 EXPECT_TRUE(controller()->IsRevealed());
964 bubble_widget4->Hide();
965 EXPECT_TRUE(controller()->IsRevealed());
966 bubble_widget5->Hide();
967 EXPECT_FALSE(controller()->IsRevealed());
968 bubble_widget5->Show();
969 EXPECT_TRUE(controller()->IsRevealed());
971 // 4) Test that visibility changes which occur while immersive fullscreen is
972 // disabled are handled upon reenabling immersive fullscreen.
974 bubble_widget5->Hide();
976 EXPECT_FALSE(controller()->IsRevealed());
978 // We do not need |bubble_widget4| or |bubble_widget5| anymore, close them.
979 bubble_widget4->Close();
980 bubble_widget5->Close();
982 // 5) Test that a bubble added while immersive fullscreen is disabled is
983 // handled upon reenabling immersive fullscreen.
986 views::Widget* bubble_widget6 = views::BubbleDelegateView::CreateBubble(
987 new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
988 bubble_widget6->Show();
991 EXPECT_TRUE(controller()->IsRevealed());
993 bubble_widget6->Close();
995 // 6) Test that a bubble which is not anchored to a child of the
996 // TopContainerView does not trigger a reveal or keep the
997 // top-of-window views revealed if they are already revealed.
998 views::Widget* bubble_widget7 = views::BubbleDelegateView::CreateBubble(
999 new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE));
1000 bubble_widget7->Show();
1001 EXPECT_FALSE(controller()->IsRevealed());
1003 // Activating |top_container_widget| will close |bubble_widget6|.
1004 top_container_widget->Activate();
1005 AttemptReveal(MODALITY_MOUSE);
1006 EXPECT_TRUE(controller()->IsRevealed());
1008 views::Widget* bubble_widget8 = views::BubbleDelegateView::CreateBubble(
1009 new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE));
1010 bubble_widget8->Show();
1012 EXPECT_FALSE(controller()->IsRevealed());
1013 bubble_widget8->Close();
1016 #endif // defined(OS_WIN)
1018 // Test that the shelf is set to auto hide as long as the window is in
1019 // immersive fullscreen and that the shelf's state before entering immersive
1020 // fullscreen is restored upon exiting immersive fullscreen.
1021 TEST_F(ImmersiveFullscreenControllerTest, Shelf) {
1022 ash::ShelfLayoutManager* shelf =
1023 ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
1025 // Shelf is visible by default.
1026 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1027 ASSERT_FALSE(controller()->IsEnabled());
1028 ASSERT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state());
1030 // Entering immersive fullscreen sets the shelf to auto hide.
1031 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
1033 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1035 // Disabling immersive fullscreen puts it back.
1037 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1038 ASSERT_FALSE(controller()->IsEnabled());
1039 EXPECT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state());
1041 // The user could toggle the shelf auto-hide behavior.
1042 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
1043 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1045 // Entering immersive fullscreen keeps auto-hide.
1046 window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
1048 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1050 // Disabling immersive fullscreen maintains the user's auto-hide selection.
1052 window()->SetProperty(aura::client::kShowStateKey,
1053 ui::SHOW_STATE_NORMAL);
1054 EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());