immersive_fullscreen_controller_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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.
4
5#include "ash/wm/immersive_fullscreen_controller.h"
6
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"
27
28// For now, immersive fullscreen is Chrome OS only.
29#if defined(OS_CHROMEOS)
30
31namespace ash {
32
33namespace {
34
35class MockImmersiveFullscreenControllerDelegate
36    : public ImmersiveFullscreenController::Delegate {
37 public:
38  MockImmersiveFullscreenControllerDelegate(views::View* top_container_view)
39      : top_container_view_(top_container_view),
40        enabled_(false),
41        visible_fraction_(1) {
42  }
43  virtual ~MockImmersiveFullscreenControllerDelegate() {}
44
45  // ImmersiveFullscreenController::Delegate overrides:
46  virtual void OnImmersiveRevealStarted() OVERRIDE {
47    enabled_ = true;
48    visible_fraction_ = 0;
49  }
50  virtual void OnImmersiveRevealEnded() OVERRIDE {
51    visible_fraction_ = 0;
52  }
53  virtual void OnImmersiveFullscreenExited() OVERRIDE {
54    enabled_ = false;
55    visible_fraction_ = 1;
56  }
57  virtual void SetVisibleFraction(double visible_fraction) OVERRIDE {
58    visible_fraction_ = visible_fraction;
59  }
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;
64  }
65
66  bool is_enabled() const {
67    return enabled_;
68  }
69
70  double visible_fraction() const {
71    return visible_fraction_;
72  }
73
74 private:
75  views::View* top_container_view_;
76  bool enabled_;
77  double visible_fraction_;
78
79  DISALLOW_COPY_AND_ASSIGN(MockImmersiveFullscreenControllerDelegate);
80};
81
82class ConsumeEventHandler : public aura::test::TestEventHandler {
83 public:
84  ConsumeEventHandler() {}
85  virtual ~ConsumeEventHandler() {}
86
87 private:
88  virtual void OnEvent(ui::Event* event) OVERRIDE {
89    aura::test::TestEventHandler::OnEvent(event);
90    if (event->cancelable())
91      event->SetHandled();
92  }
93
94  DISALLOW_COPY_AND_ASSIGN(ConsumeEventHandler);
95};
96
97}  // namespace
98
99/////////////////////////////////////////////////////////////////////////////
100
101class ImmersiveFullscreenControllerTest : public ash::test::AshTestBase {
102 public:
103  enum Modality {
104    MODALITY_MOUSE,
105    MODALITY_GESTURE_TAP,
106    MODALITY_GESTURE_SCROLL
107  };
108
109  ImmersiveFullscreenControllerTest()
110      : widget_(NULL),
111        top_container_(NULL),
112        content_view_(NULL) {}
113  virtual ~ImmersiveFullscreenControllerTest() {}
114
115  ImmersiveFullscreenController* controller() {
116    return controller_.get();
117  }
118
119  views::NativeViewHost* content_view() {
120    return content_view_;
121  }
122
123  views::View* top_container() {
124    return top_container_;
125  }
126
127  views::Widget* widget() { return widget_; }
128
129  aura::Window* window() {
130    return widget_->GetNativeWindow();
131  }
132
133  MockImmersiveFullscreenControllerDelegate* delegate() {
134    return delegate_.get();
135  }
136
137  // Access to private data from the controller.
138  bool top_edge_hover_timer_running() const {
139    return controller_->top_edge_hover_timer_.IsRunning();
140  }
141  int mouse_x_when_hit_top() const {
142    return controller_->mouse_x_when_hit_top_in_screen_;
143  }
144
145  // ash::test::AshTestBase overrides:
146  virtual void SetUp() OVERRIDE {
147    ash::test::AshTestBase::SetUp();
148
149    widget_ = new views::Widget();
150    views::Widget::InitParams params;
151    params.context = CurrentContext();
152    widget_->Init(params);
153    widget_->Show();
154
155    window()->SetProperty(aura::client::kShowStateKey,
156                          ui::SHOW_STATE_FULLSCREEN);
157
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_);
162
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_);
168
169    delegate_.reset(
170        new MockImmersiveFullscreenControllerDelegate(top_container_));
171    controller_.reset(new ImmersiveFullscreenController);
172    controller_->Init(delegate_.get(), widget_, top_container_);
173    controller_->SetupForTest();
174
175    // The mouse is moved so that it is not over |top_container_| by
176    // AshTestBase.
177  }
178
179  // Enables / disables immersive fullscreen.
180  void SetEnabled(bool enabled) {
181    controller_->SetEnabled(ImmersiveFullscreenController::WINDOW_TYPE_OTHER,
182                            enabled);
183  }
184
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);
190  }
191
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);
196  }
197
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);
203  }
204
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());
211
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
215    // the screen.
216    if (controller()->top_edge_hover_timer_.IsRunning()) {
217      controller()->top_edge_hover_timer_.user_task().Run();
218      controller()->top_edge_hover_timer_.Stop();
219    }
220  }
221
222 private:
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);
227    switch (modality) {
228      case MODALITY_MOUSE: {
229        MoveMouse(event_position.x(), event_position.y());
230        break;
231      }
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();
239        break;
240      }
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(
249            start, end,
250            base::TimeDelta::FromMilliseconds(30), 1);
251        break;
252      }
253    }
254  }
255
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.
261
262  DISALLOW_COPY_AND_ASSIGN(ImmersiveFullscreenControllerTest);
263};
264
265// Test the initial state and that the delegate gets notified of the
266// top-of-window views getting hidden and revealed.
267TEST_F(ImmersiveFullscreenControllerTest, Delegate) {
268  // Initial state.
269  EXPECT_FALSE(controller()->IsEnabled());
270  EXPECT_FALSE(controller()->IsRevealed());
271  EXPECT_FALSE(delegate()->is_enabled());
272
273  // Enabling initially hides the top views.
274  SetEnabled(true);
275  EXPECT_TRUE(controller()->IsEnabled());
276  EXPECT_FALSE(controller()->IsRevealed());
277  EXPECT_TRUE(delegate()->is_enabled());
278  EXPECT_EQ(0, delegate()->visible_fraction());
279
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());
286
287  // Disabling ends the immersive reveal.
288  SetEnabled(false);
289  EXPECT_FALSE(controller()->IsEnabled());
290  EXPECT_FALSE(controller()->IsRevealed());
291  EXPECT_FALSE(delegate()->is_enabled());
292}
293
294// GetRevealedLock() specific tests.
295TEST_F(ImmersiveFullscreenControllerTest, RevealedLock) {
296  scoped_ptr<ImmersiveRevealedLock> lock1;
297  scoped_ptr<ImmersiveRevealedLock> lock2;
298
299  // Immersive fullscreen is not on by default.
300  EXPECT_FALSE(controller()->IsEnabled());
301
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());
309
310  // Immersive fullscreen should start in the revealed state due to the lock.
311  SetEnabled(true);
312  EXPECT_TRUE(controller()->IsEnabled());
313  EXPECT_TRUE(controller()->IsRevealed());
314
315  SetEnabled(false);
316  EXPECT_FALSE(controller()->IsEnabled());
317  EXPECT_FALSE(controller()->IsRevealed());
318
319  lock1.reset();
320  EXPECT_FALSE(controller()->IsEnabled());
321  EXPECT_FALSE(controller()->IsRevealed());
322
323  // Immersive fullscreen should start in the closed state because the lock is
324  // no longer held.
325  SetEnabled(true);
326  EXPECT_TRUE(controller()->IsEnabled());
327  EXPECT_FALSE(controller()->IsRevealed());
328
329  // 2) Test that acquiring a lock reveals the top-of-window views if they are
330  // hidden.
331  lock1.reset(controller()->GetRevealedLock(
332      ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
333  EXPECT_TRUE(controller()->IsRevealed());
334
335  // 3) Test that the top-of-window views are only hidden when all of the locks
336  // are released.
337  lock2.reset(controller()->GetRevealedLock(
338      ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
339  lock1.reset();
340  EXPECT_TRUE(controller()->IsRevealed());
341
342  lock2.reset();
343  EXPECT_FALSE(controller()->IsRevealed());
344}
345
346// Test mouse event processing for top-of-screen reveal triggering.
347TEST_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(
352      display_layout);
353
354  // Set up initial state.
355  SetEnabled(true);
356  ASSERT_TRUE(controller()->IsEnabled());
357  ASSERT_FALSE(controller()->IsRevealed());
358
359  aura::test::EventGenerator& event_generator(GetEventGenerator());
360
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());
366
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());
372
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());
378
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());
383
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());
388
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());
393
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());
398
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());
404
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());
409
410  // Once revealed, a move just a little below the top container doesn't end a
411  // reveal.
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());
416
417  // Once revealed, clicking just below the top container ends the reveal.
418  event_generator.ClickLeftButton();
419  EXPECT_FALSE(controller()->IsRevealed());
420
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());
427
428  // The mouse position cannot cause a reveal when the top container's widget
429  // has capture.
430  views::Widget* widget = top_container()->GetWidget();
431  widget->SetCapture(top_container());
432  AttemptReveal(MODALITY_MOUSE);
433  EXPECT_FALSE(controller()->IsRevealed());
434  widget->ReleaseCapture();
435
436  // The mouse position cannot end the reveal while the top container's widget
437  // has capture.
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());
444
445  // Releasing capture should end the reveal.
446  widget->ReleaseCapture();
447  EXPECT_FALSE(controller()->IsRevealed());
448}
449
450// Test mouse event processing for top-of-screen reveal triggering when the
451// top container's widget is inactive.
452TEST_F(ImmersiveFullscreenControllerTest, Inactive) {
453  // Set up initial state.
454  views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds(
455      NULL,
456      CurrentContext(),
457      gfx::Rect(0, 0, 200, 200));
458  popup_widget->Show();
459  ASSERT_FALSE(top_container()->GetWidget()->IsActive());
460
461  SetEnabled(true);
462  ASSERT_TRUE(controller()->IsEnabled());
463  ASSERT_FALSE(controller()->IsRevealed());
464
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());
472
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());
478
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());
484
485  // The top-of-window views should stay revealed if the cursor is moved off
486  // of the top edge.
487  MoveMouse(top_container_bounds_in_screen.right() - 1,
488            top_container_bounds_in_screen.bottom() - 1);
489  EXPECT_TRUE(controller()->IsRevealed());
490
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());
495
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());
506}
507
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.
511TEST_F(ImmersiveFullscreenControllerTest, MouseEventsVerticalDisplayLayout) {
512  if (!SupportsMultipleDisplays())
513    return;
514
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(
519      display_layout);
520
521  SetEnabled(true);
522  ASSERT_TRUE(controller()->IsEnabled());
523  ASSERT_FALSE(controller()->IsRevealed());
524
525  aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
526  ASSERT_EQ(root_windows[0],
527            top_container()->GetWidget()->GetNativeWindow()->GetRootWindow());
528
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();
536
537  aura::test::EventGenerator& event_generator(GetEventGenerator());
538
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
541  // running.
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());
546
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());
553
554  // The timer should continue running if the user overshoots the top edge
555  // a bit.
556  event_generator.MoveMouseTo(x, y_top_edge - 2);
557  EXPECT_TRUE(top_edge_hover_timer_running());
558
559  // The timer should stop running if the user overshoots the top edge by
560  // a lot.
561  event_generator.MoveMouseTo(x, y_top_edge - 20);
562  EXPECT_FALSE(top_edge_hover_timer_running());
563
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);
567
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());
573
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());
578
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());
583
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(
587      NULL,
588      CurrentContext(),
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());
597}
598
599// Test behavior when the mouse becomes hovered without moving.
600TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) {
601  SetEnabled(true);
602  scoped_ptr<ImmersiveRevealedLock> lock;
603
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.
608  SetHovered(true);
609  EXPECT_FALSE(controller()->IsRevealed());
610  lock.reset(controller()->GetRevealedLock(
611      ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
612  EXPECT_TRUE(controller()->IsRevealed());
613  lock.reset();
614  EXPECT_TRUE(controller()->IsRevealed());
615  SetHovered(false);
616  EXPECT_FALSE(controller()->IsRevealed());
617
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.
622  SetEnabled(false);
623  SetHovered(true);
624  EXPECT_FALSE(controller()->IsRevealed());
625  SetEnabled(true);
626  EXPECT_FALSE(controller()->IsRevealed());
627
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.
632  SetEnabled(false);
633  SetHovered(true);
634  lock.reset(controller()->GetRevealedLock(
635      ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
636  EXPECT_FALSE(controller()->IsRevealed());
637  SetEnabled(true);
638  EXPECT_TRUE(controller()->IsRevealed());
639  lock.reset();
640  EXPECT_TRUE(controller()->IsRevealed());
641  SetHovered(false);
642  EXPECT_FALSE(controller()->IsRevealed());
643}
644
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.
649TEST_F(ImmersiveFullscreenControllerTest, DifferentModalityEnterExit) {
650  SetEnabled(true);
651  EXPECT_TRUE(controller()->IsEnabled());
652  EXPECT_FALSE(controller()->IsRevealed());
653
654  // Initiate reveal via gesture, end reveal via mouse.
655  AttemptReveal(MODALITY_GESTURE_SCROLL);
656  EXPECT_TRUE(controller()->IsRevealed());
657  MoveMouse(1, 1);
658  EXPECT_TRUE(controller()->IsRevealed());
659  AttemptUnreveal(MODALITY_MOUSE);
660  EXPECT_FALSE(controller()->IsRevealed());
661
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());
667
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());
673
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());
679}
680
681// Test when the SWIPE_CLOSE edge gesture closes the top-of-window views.
682TEST_F(ImmersiveFullscreenControllerTest, EndRevealViaGesture) {
683  SetEnabled(true);
684  EXPECT_TRUE(controller()->IsEnabled());
685  EXPECT_FALSE(controller()->IsRevealed());
686
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());
694
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());
699
700  // If some other code is holding onto a lock, a gesture should not be able to
701  // end the reveal.
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());
708  lock.reset();
709  EXPECT_FALSE(controller()->IsRevealed());
710}
711
712// Tests that touch-gesture can be used to reveal the top-of-window views when
713// the child window consumes all events.
714TEST_F(ImmersiveFullscreenControllerTest, RevealViaGestureChildConsumesEvents) {
715  // Enabling initially hides the top views.
716  SetEnabled(true);
717  EXPECT_TRUE(controller()->IsEnabled());
718  EXPECT_FALSE(controller()->IsRevealed());
719
720  aura::test::TestWindowDelegate child_delegate;
721  scoped_ptr<aura::Window> child(
722      CreateTestWindowInShellWithDelegateAndType(&child_delegate,
723                                                 ui::wm::WINDOW_TYPE_CONTROL,
724                                                 1234,
725                                                 gfx::Rect()));
726  content_view()->Attach(child.get());
727  child->Show();
728
729  ConsumeEventHandler handler;
730  child->AddPreTargetHandler(&handler);
731
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());
737
738  AttemptUnreveal(MODALITY_GESTURE_TAP);
739  EXPECT_FALSE(controller()->IsRevealed());
740  EXPECT_GT(handler.num_touch_events(), 0);
741  child->RemovePreTargetHandler(&handler);
742}
743
744// Make sure touch events towards the top of the window do not leak through to
745// windows underneath.
746TEST_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));
751  behind->Show();
752  behind->SetBounds(window_bounds);
753  widget()->StackAbove(behind.get());
754
755  // Make sure the windows are aligned on top.
756  EXPECT_EQ(behind->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
757  int top = behind->GetBoundsInScreen().y();
758
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));
764
765  SetEnabled(true);
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));
774}
775
776// Do not test under windows because focus testing is not reliable on
777// Windows. (crbug.com/79493)
778#if !defined(OS_WIN)
779
780// Test how focus and activation affects whether the top-of-window views are
781// revealed.
782TEST_F(ImmersiveFullscreenControllerTest, Focus) {
783  // Add views to the view hierarchy which we will focus and unfocus during the
784  // test.
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();
795
796  SetEnabled(true);
797
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
800  // views.
801  AttemptReveal(MODALITY_MOUSE);
802  child_view->RequestFocus();
803  focus_manager->ClearFocus();
804  EXPECT_TRUE(controller()->IsRevealed());
805  child_view->RequestFocus();
806  SetHovered(false);
807  EXPECT_TRUE(controller()->IsRevealed());
808  focus_manager->ClearFocus();
809  EXPECT_FALSE(controller()->IsRevealed());
810
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
814  // revealed.
815  child_view->RequestFocus();
816  EXPECT_TRUE(controller()->IsRevealed());
817  unrelated_view->RequestFocus();
818  EXPECT_FALSE(controller()->IsRevealed());
819
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());
824  SetEnabled(false);
825  EXPECT_FALSE(controller()->IsRevealed());
826  unrelated_view->RequestFocus();
827  SetEnabled(true);
828  EXPECT_FALSE(controller()->IsRevealed());
829
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());
834  SetEnabled(false);
835  scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock(
836      ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
837  EXPECT_FALSE(controller()->IsRevealed());
838  unrelated_view->RequestFocus();
839  SetEnabled(true);
840  EXPECT_TRUE(controller()->IsRevealed());
841  lock.reset();
842  EXPECT_FALSE(controller()->IsRevealed());
843}
844
845// Test how transient windows affect whether the top-of-window views are
846// revealed.
847TEST_F(ImmersiveFullscreenControllerTest, Transient) {
848  views::Widget* top_container_widget = top_container()->GetWidget();
849
850  SetEnabled(true);
851  ASSERT_FALSE(controller()->IsRevealed());
852
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
855  // revealed.
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);
863
864  EXPECT_FALSE(controller()->IsRevealed());
865  AttemptReveal(MODALITY_MOUSE);
866  EXPECT_TRUE(controller()->IsRevealed());
867  transient_widget->Show();
868  SetHovered(false);
869  EXPECT_TRUE(controller()->IsRevealed());
870  transient_widget.reset();
871  EXPECT_FALSE(controller()->IsRevealed());
872
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);
882
883  EXPECT_FALSE(controller()->IsRevealed());
884  AttemptReveal(MODALITY_MOUSE);
885  EXPECT_TRUE(controller()->IsRevealed());
886  non_transient_widget->Show();
887  SetHovered(false);
888  EXPECT_FALSE(controller()->IsRevealed());
889}
890
891// Test how bubbles affect whether the top-of-window views are revealed.
892TEST_F(ImmersiveFullscreenControllerTest, Bubbles) {
893  scoped_ptr<ImmersiveRevealedLock> revealed_lock;
894  views::Widget* top_container_widget = top_container()->GetWidget();
895
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);
903
904  SetEnabled(true);
905  ASSERT_FALSE(controller()->IsRevealed());
906
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
909  // its visibility.
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());
914
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());
921
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();
927  SetHovered(false);
928  EXPECT_TRUE(controller()->IsRevealed());
929  bubble_widget2->Close();
930  EXPECT_FALSE(controller()->IsRevealed());
931
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();
938  SetHovered(true);
939  EXPECT_TRUE(controller()->IsRevealed());
940  top_container_widget->Activate();
941  EXPECT_TRUE(controller()->IsRevealed());
942
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.
945  SetHovered(false);
946  EXPECT_FALSE(controller()->IsRevealed());
947
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(
952      bubble_delegate4));
953  bubble_widget4->Show();
954
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(
959      bubble_delegate5));
960  bubble_widget5->Show();
961
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());
969
970  // 4) Test that visibility changes which occur while immersive fullscreen is
971  // disabled are handled upon reenabling immersive fullscreen.
972  SetEnabled(false);
973  bubble_widget5->Hide();
974  SetEnabled(true);
975  EXPECT_FALSE(controller()->IsRevealed());
976
977  // We do not need |bubble_widget4| or |bubble_widget5| anymore, close them.
978  bubble_widget4->Close();
979  bubble_widget5->Close();
980
981  // 5) Test that a bubble added while immersive fullscreen is disabled is
982  // handled upon reenabling immersive fullscreen.
983  SetEnabled(false);
984
985  views::Widget* bubble_widget6 = views::BubbleDelegateView::CreateBubble(
986      new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
987  bubble_widget6->Show();
988
989  SetEnabled(true);
990  EXPECT_TRUE(controller()->IsRevealed());
991
992  bubble_widget6->Close();
993
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());
1001
1002  // Activating |top_container_widget| will close |bubble_widget6|.
1003  top_container_widget->Activate();
1004  AttemptReveal(MODALITY_MOUSE);
1005  EXPECT_TRUE(controller()->IsRevealed());
1006
1007  views::Widget* bubble_widget8 = views::BubbleDelegateView::CreateBubble(
1008      new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE));
1009  bubble_widget8->Show();
1010  SetHovered(false);
1011  EXPECT_FALSE(controller()->IsRevealed());
1012  bubble_widget8->Close();
1013}
1014
1015#endif  // defined(OS_WIN)
1016
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.
1020TEST_F(ImmersiveFullscreenControllerTest, Shelf) {
1021  ash::internal::ShelfLayoutManager* shelf =
1022      ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
1023
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());
1028
1029  // Entering immersive fullscreen sets the shelf to auto hide.
1030  window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
1031  SetEnabled(true);
1032  EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1033
1034  // Disabling immersive fullscreen puts it back.
1035  SetEnabled(false);
1036  window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1037  ASSERT_FALSE(controller()->IsEnabled());
1038  EXPECT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state());
1039
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());
1043
1044  // Entering immersive fullscreen keeps auto-hide.
1045  window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
1046  SetEnabled(true);
1047  EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1048
1049  // Disabling immersive fullscreen maintains the user's auto-hide selection.
1050  SetEnabled(false);
1051  window()->SetProperty(aura::client::kShowStateKey,
1052                        ui::SHOW_STATE_NORMAL);
1053  EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1054}
1055
1056}  // namespase ash
1057
1058#endif  // defined(OS_CHROMEOS)
1059