immersive_mode_controller_ash_unittest.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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 "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
6
7#include "ash/display/display_controller.h"
8#include "ash/shell.h"
9#include "ash/test/ash_test_base.h"
10#include "chrome/browser/ui/immersive_fullscreen_configuration.h"
11#include "ui/aura/client/cursor_client.h"
12#include "ui/aura/env.h"
13#include "ui/aura/root_window.h"
14#include "ui/aura/test/event_generator.h"
15#include "ui/aura/window.h"
16#include "ui/gfx/animation/slide_animation.h"
17#include "ui/views/bubble/bubble_delegate.h"
18
19// For now, immersive fullscreen is Chrome OS only.
20#if defined(OS_CHROMEOS)
21
22/////////////////////////////////////////////////////////////////////////////
23
24class MockImmersiveModeControllerDelegate
25    : public ImmersiveModeController::Delegate {
26 public:
27  MockImmersiveModeControllerDelegate() : immersive_style_(false) {}
28  virtual ~MockImmersiveModeControllerDelegate() {}
29
30  bool immersive_style() const { return immersive_style_; }
31
32  // ImmersiveModeController::Delegate overrides:
33  virtual BookmarkBarView* GetBookmarkBar() OVERRIDE { return NULL; }
34  virtual FullscreenController* GetFullscreenController() OVERRIDE {
35    return NULL;
36  }
37  virtual void FullscreenStateChanged() OVERRIDE {}
38  virtual void SetImmersiveStyle(bool immersive) OVERRIDE {
39    immersive_style_ = immersive;
40  }
41  virtual content::WebContents* GetWebContents() OVERRIDE {
42    return NULL;
43  }
44
45 private:
46  bool immersive_style_;
47
48  DISALLOW_COPY_AND_ASSIGN(MockImmersiveModeControllerDelegate);
49};
50
51/////////////////////////////////////////////////////////////////////////////
52
53class ImmersiveModeControllerAshTest : public ash::test::AshTestBase {
54 public:
55  enum Modality {
56    MODALITY_MOUSE,
57    MODALITY_TOUCH,
58    MODALITY_GESTURE
59  };
60
61  ImmersiveModeControllerAshTest() : widget_(NULL), top_container_(NULL) {}
62  virtual ~ImmersiveModeControllerAshTest() {}
63
64  ImmersiveModeControllerAsh* controller() { return controller_.get(); }
65  views::View* top_container() { return top_container_; }
66  MockImmersiveModeControllerDelegate* delegate() { return delegate_.get(); }
67
68  // Access to private data from the controller.
69  bool top_edge_hover_timer_running() const {
70    return controller_->top_edge_hover_timer_.IsRunning();
71  }
72  int mouse_x_when_hit_top() const {
73    return controller_->mouse_x_when_hit_top_in_screen_;
74  }
75
76  // ash::test::AshTestBase overrides:
77  virtual void SetUp() OVERRIDE {
78    ash::test::AshTestBase::SetUp();
79
80    ImmersiveFullscreenConfiguration::EnableImmersiveFullscreenForTest();
81    ASSERT_TRUE(ImmersiveFullscreenConfiguration::UseImmersiveFullscreen());
82
83    controller_.reset(new ImmersiveModeControllerAsh);
84    delegate_.reset(new MockImmersiveModeControllerDelegate);
85
86    widget_ = new views::Widget();
87    views::Widget::InitParams params;
88    params.context = CurrentContext();
89    params.bounds = gfx::Rect(0, 0, 500, 500);
90    widget_->Init(params);
91    widget_->Show();
92
93    top_container_ = new views::View();
94    top_container_->SetBounds(0, 0, 500, 100);
95    top_container_->set_focusable(true);
96
97    widget_->GetContentsView()->AddChildView(top_container_);
98
99    controller_->Init(delegate_.get(), widget_, top_container_);
100    SetAnimationsDisabled(true);
101  }
102
103  // Enable or disable the immersive mode controller's animations. When the
104  // immersive mode controller's animations are disabled, some behavior is
105  // slightly different. In particular, the behavior is different when there
106  // is a transfer in which lock keeps the top-of-window views revealed (eg
107  // bubble keeps top-of-window views revealed -> mouse keeps top-of-window
108  // views revealed). It is necessary to temparily enable the immersive
109  // controller's animations to get the correct behavior in tests.
110  void SetAnimationsDisabled(bool disabled) {
111    controller_->animations_disabled_for_test_ = disabled;
112    // Force any in progress animations to finish.
113    if (disabled)
114      controller_->animation_->End();
115  }
116
117  // Attempt to reveal the top-of-window views via |modality|.
118  // The top-of-window views can only be revealed via mouse hover or a gesture.
119  void AttemptReveal(Modality modality) {
120    ASSERT_NE(modality, MODALITY_TOUCH);
121    AttemptRevealStateChange(true, modality);
122  }
123
124  // Attempt to unreveal the top-of-window views via |modality|. The
125  // top-of-window views can be unrevealed via any modality.
126  void AttemptUnreveal(Modality modality) {
127    AttemptRevealStateChange(false, modality);
128  }
129
130  // Sets whether the mouse is hovered above |top_container_|.
131  // SetHovered(true) moves the mouse over the |top_container_| but does not
132  // move it to the top of the screen so will not initiate a reveal.
133  void SetHovered(bool is_mouse_hovered) {
134    MoveMouse(0, is_mouse_hovered ? 1 : top_container_->height() + 100);
135  }
136
137  // Move the mouse to the given coordinates. The coordinates should be in
138  // |top_container_| coordinates.
139  void MoveMouse(int x, int y) {
140    gfx::Point screen_position(x, y);
141    views::View::ConvertPointToScreen(top_container_, &screen_position);
142    GetEventGenerator().MoveMouseTo(screen_position.x(), screen_position.y());
143
144    // If the top edge timer started running as a result of the mouse move, run
145    // the task which occurs after the timer delay. This reveals the
146    // top-of-window views synchronously if the mouse is hovered at the top of
147    // the screen.
148    if (controller()->top_edge_hover_timer_.IsRunning()) {
149      controller()->top_edge_hover_timer_.user_task().Run();
150      controller()->top_edge_hover_timer_.Stop();
151    }
152  }
153
154 private:
155  // Attempt to change the revealed state to |revealed| via |modality|.
156  void AttemptRevealStateChange(bool revealed, Modality modality) {
157    // Compute the event position in |top_container_| coordinates.
158    gfx::Point event_position(0, revealed ? 0 : top_container_->height() + 100);
159    switch (modality) {
160      case MODALITY_MOUSE: {
161        MoveMouse(event_position.x(), event_position.y());
162        break;
163      }
164      case MODALITY_TOUCH: {
165        gfx::Point screen_position = event_position;
166        views::View::ConvertPointToScreen(top_container_, &screen_position);
167
168        aura::test::EventGenerator& event_generator(GetEventGenerator());
169        event_generator.MoveTouch(event_position);
170        event_generator.PressTouch();
171        event_generator.ReleaseTouch();
172        break;
173      }
174      case MODALITY_GESTURE: {
175        aura::client::GetCursorClient(CurrentContext())->DisableMouseEvents();
176        ImmersiveModeControllerAsh::SwipeType swipe_type = revealed ?
177            ImmersiveModeControllerAsh::SWIPE_OPEN :
178            ImmersiveModeControllerAsh::SWIPE_CLOSE;
179        controller_->UpdateRevealedLocksForSwipe(swipe_type);
180        break;
181      }
182    }
183  }
184
185  scoped_ptr<ImmersiveModeControllerAsh> controller_;
186  scoped_ptr<MockImmersiveModeControllerDelegate> delegate_;
187  views::Widget* widget_;  // Owned by the native widget.
188  views::View* top_container_;  // Owned by |root_view_|.
189  scoped_ptr<aura::test::EventGenerator> event_generator_;
190
191  DISALLOW_COPY_AND_ASSIGN(ImmersiveModeControllerAshTest);
192};
193
194// Test of initial state and basic functionality.
195TEST_F(ImmersiveModeControllerAshTest, ImmersiveModeControllerAsh) {
196  // Initial state.
197  EXPECT_FALSE(controller()->IsEnabled());
198  EXPECT_FALSE(controller()->ShouldHideTopViews());
199  EXPECT_FALSE(controller()->IsRevealed());
200  EXPECT_FALSE(delegate()->immersive_style());
201
202  // Enabling hides the top views.
203  controller()->SetEnabled(true);
204  EXPECT_TRUE(controller()->IsEnabled());
205  EXPECT_FALSE(controller()->IsRevealed());
206  EXPECT_TRUE(controller()->ShouldHideTopViews());
207  EXPECT_FALSE(controller()->ShouldHideTabIndicators());
208  EXPECT_TRUE(delegate()->immersive_style());
209
210  // Revealing shows the top views.
211  AttemptReveal(MODALITY_MOUSE);
212  EXPECT_TRUE(controller()->IsRevealed());
213  EXPECT_FALSE(controller()->ShouldHideTopViews());
214  // Tabs are painting in the normal style during a reveal.
215  EXPECT_FALSE(delegate()->immersive_style());
216}
217
218// Test mouse event processing for top-of-screen reveal triggering.
219TEST_F(ImmersiveModeControllerAshTest, OnMouseEvent) {
220  // Set up initial state.
221  controller()->SetEnabled(true);
222  ASSERT_TRUE(controller()->IsEnabled());
223  ASSERT_FALSE(controller()->IsRevealed());
224
225  aura::test::EventGenerator& event_generator(GetEventGenerator());
226
227  gfx::Rect top_container_bounds_in_screen =
228      top_container()->GetBoundsInScreen();
229  // A position along the top edge of TopContainerView in screen coordinates.
230  gfx::Point top_edge_pos(top_container_bounds_in_screen.x() + 100,
231                          top_container_bounds_in_screen.y());
232
233  // Mouse wheel event does nothing.
234  ui::MouseEvent wheel(
235      ui::ET_MOUSEWHEEL, top_edge_pos, top_edge_pos, ui::EF_NONE);
236  event_generator.Dispatch(&wheel);
237  EXPECT_FALSE(top_edge_hover_timer_running());
238
239  // Move to top edge of screen starts hover timer running. We cannot use
240  // MoveMouse() because MoveMouse() stops the timer if it started running.
241  event_generator.MoveMouseTo(top_edge_pos);
242  EXPECT_TRUE(top_edge_hover_timer_running());
243  EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top());
244
245  // Moving |ImmersiveModeControllerAsh::kMouseRevealBoundsHeight| down from
246  // the top edge stops it.
247  event_generator.MoveMouseBy(0, 3);
248  EXPECT_FALSE(top_edge_hover_timer_running());
249
250  // Moving back to the top starts the timer again.
251  event_generator.MoveMouseTo(top_edge_pos);
252  EXPECT_TRUE(top_edge_hover_timer_running());
253  EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top());
254
255  // Slight move to the right keeps the timer running for the same hit point.
256  event_generator.MoveMouseBy(1, 0);
257  EXPECT_TRUE(top_edge_hover_timer_running());
258  EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top());
259
260  // Moving back to the left also keeps the timer running.
261  event_generator.MoveMouseBy(-1, 0);
262  EXPECT_TRUE(top_edge_hover_timer_running());
263  EXPECT_EQ(top_edge_pos.x(), mouse_x_when_hit_top());
264
265  // Large move right restarts the timer (so it is still running) and considers
266  // this a new hit at the top.
267  event_generator.MoveMouseTo(top_edge_pos.x() + 100, top_edge_pos.y());
268  EXPECT_TRUE(top_edge_hover_timer_running());
269  EXPECT_EQ(top_edge_pos.x() + 100, mouse_x_when_hit_top());
270
271  // Moving off the top edge horizontally stops the timer.
272  EXPECT_GT(CurrentContext()->bounds().width(), top_container()->width());
273  event_generator.MoveMouseTo(top_container_bounds_in_screen.right(),
274                              top_container_bounds_in_screen.y());
275  EXPECT_FALSE(top_edge_hover_timer_running());
276
277  // Once revealed, a move just a little below the top container doesn't end a
278  // reveal.
279  AttemptReveal(MODALITY_MOUSE);
280  event_generator.MoveMouseTo(top_container_bounds_in_screen.x(),
281                              top_container_bounds_in_screen.bottom() + 1);
282  EXPECT_TRUE(controller()->IsRevealed());
283
284  // Once revealed, clicking just below the top container ends the reveal.
285  event_generator.ClickLeftButton();
286  EXPECT_FALSE(controller()->IsRevealed());
287
288  // Moving a lot below the top container ends a reveal.
289  AttemptReveal(MODALITY_MOUSE);
290  EXPECT_TRUE(controller()->IsRevealed());
291  event_generator.MoveMouseTo(top_container_bounds_in_screen.x(),
292                              top_container_bounds_in_screen.bottom() + 50);
293  EXPECT_FALSE(controller()->IsRevealed());
294
295  // The mouse position cannot cause a reveal when TopContainerView's widget
296  // has capture.
297  views::Widget* widget = top_container()->GetWidget();
298  widget->SetCapture(top_container());
299  AttemptReveal(MODALITY_MOUSE);
300  EXPECT_FALSE(controller()->IsRevealed());
301  widget->ReleaseCapture();
302
303  // The mouse position cannot end the reveal while TopContainerView's widget
304  // has capture.
305  AttemptReveal(MODALITY_MOUSE);
306  EXPECT_TRUE(controller()->IsRevealed());
307  widget->SetCapture(top_container());
308  event_generator.MoveMouseTo(top_container_bounds_in_screen.x(),
309                              top_container_bounds_in_screen.bottom() + 51);
310  EXPECT_TRUE(controller()->IsRevealed());
311
312  // Releasing capture should end the reveal.
313  widget->ReleaseCapture();
314  EXPECT_FALSE(controller()->IsRevealed());
315}
316
317// Test mouse event processing for top-of-screen reveal triggering when the user
318// has a vertical display layout (primary display above/below secondary display)
319// and the immersive fullscreen window is on the bottom display.
320TEST_F(ImmersiveModeControllerAshTest, MouseEventsVerticalDisplayLayout) {
321  if (!SupportsMultipleDisplays())
322    return;
323
324  // Set up initial state.
325  UpdateDisplay("800x600,800x600");
326  ash::DisplayLayout display_layout(ash::DisplayLayout::TOP, 0);
327  ash::Shell::GetInstance()->display_controller()->SetLayoutForCurrentDisplays(
328      display_layout);
329
330  controller()->SetEnabled(true);
331  ASSERT_TRUE(controller()->IsEnabled());
332  ASSERT_FALSE(controller()->IsRevealed());
333
334  ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
335  ASSERT_EQ(root_windows[0],
336            top_container()->GetWidget()->GetNativeWindow()->GetRootWindow());
337
338  gfx::Rect primary_root_window_bounds_in_screen =
339      root_windows[0]->GetBoundsInScreen();
340  // Do not set |x| to the root window's x position because the display's
341  // corners have special behavior.
342  int x = primary_root_window_bounds_in_screen.x() + 10;
343  // The y position of the top edge of the primary display.
344  int y_top_edge = primary_root_window_bounds_in_screen.y();
345
346  aura::test::EventGenerator& event_generator(GetEventGenerator());
347
348  // Moving right below the top edge starts the hover timer running. We
349  // cannot use MoveMouse() because MoveMouse() stops the timer if it started
350  // running.
351  event_generator.MoveMouseTo(x, y_top_edge + 1);
352  EXPECT_TRUE(top_edge_hover_timer_running());
353  EXPECT_EQ(y_top_edge + 1,
354            aura::Env::GetInstance()->last_mouse_location().y());
355
356  // The timer should continue running if the user moves the mouse to the top
357  // edge even though the mouse is warped to the secondary display.
358  event_generator.MoveMouseTo(x, y_top_edge);
359  EXPECT_TRUE(top_edge_hover_timer_running());
360  EXPECT_NE(y_top_edge,
361            aura::Env::GetInstance()->last_mouse_location().y());
362
363  // The timer should continue running if the user overshoots the top edge
364  // a bit.
365  event_generator.MoveMouseTo(x, y_top_edge - 2);
366  EXPECT_TRUE(top_edge_hover_timer_running());
367
368  // The timer should stop running if the user overshoots the top edge by
369  // a lot.
370  event_generator.MoveMouseTo(x, y_top_edge - 20);
371  EXPECT_FALSE(top_edge_hover_timer_running());
372
373  // The timer should not start if the user moves the mouse to the bottom of the
374  // secondary display without crossing the top edge first.
375  event_generator.MoveMouseTo(x, y_top_edge - 2);
376
377  // Reveal the top-of-window views by overshooting the top edge slightly.
378  event_generator.MoveMouseTo(x, y_top_edge + 1);
379  // MoveMouse() runs the timer task.
380  MoveMouse(x, y_top_edge - 2);
381  EXPECT_TRUE(controller()->IsRevealed());
382
383  // The top-of-window views should stay revealed if the user moves the mouse
384  // around in the bottom region of the secondary display.
385  event_generator.MoveMouseTo(x + 10, y_top_edge - 3);
386  EXPECT_TRUE(controller()->IsRevealed());
387
388  // The top-of-window views should hide if the user moves the mouse away from
389  // the bottom region of the secondary display.
390  event_generator.MoveMouseTo(x, y_top_edge - 20);
391  EXPECT_FALSE(controller()->IsRevealed());
392}
393
394// Test that hovering the mouse over the find bar does not end a reveal.
395TEST_F(ImmersiveModeControllerAshTest, FindBar) {
396  // Set up initial state.
397  controller()->SetEnabled(true);
398  ASSERT_TRUE(controller()->IsEnabled());
399  ASSERT_FALSE(controller()->IsRevealed());
400
401  // Compute the find bar bounds relative to TopContainerView. The find
402  // bar is aligned with the bottom right of the TopContainerView.
403  gfx::Rect find_bar_bounds(top_container()->bounds().right() - 100,
404                            top_container()->bounds().bottom(),
405                            100,
406                            50);
407
408  gfx::Point find_bar_position_in_screen = find_bar_bounds.origin();
409  views::View::ConvertPointToScreen(top_container(),
410      &find_bar_position_in_screen);
411  gfx::Rect find_bar_bounds_in_screen(find_bar_position_in_screen,
412      find_bar_bounds.size());
413  controller()->OnFindBarVisibleBoundsChanged(find_bar_bounds_in_screen);
414
415  // Moving the mouse over the find bar does not end the reveal.
416  gfx::Point over_find_bar(find_bar_bounds.x() + 25, find_bar_bounds.y() + 25);
417  AttemptReveal(MODALITY_MOUSE);
418  EXPECT_TRUE(controller()->IsRevealed());
419  MoveMouse(over_find_bar.x(), over_find_bar.y());
420  EXPECT_TRUE(controller()->IsRevealed());
421
422  // Moving the mouse off of the find bar horizontally ends the reveal.
423  MoveMouse(find_bar_bounds.x() - 25, find_bar_bounds.y() + 25);
424  EXPECT_FALSE(controller()->IsRevealed());
425
426  // Moving the mouse off of the find bar vertically ends the reveal.
427  AttemptReveal(MODALITY_MOUSE);
428  EXPECT_TRUE(controller()->IsRevealed());
429  MoveMouse(find_bar_bounds.x() + 25, find_bar_bounds.bottom() + 25);
430
431  // Similar to the TopContainerView, moving the mouse slightly off vertically
432  // of the find bar does not end the reveal.
433  AttemptReveal(MODALITY_MOUSE);
434  MoveMouse(find_bar_bounds.x() + 25, find_bar_bounds.bottom() + 1);
435  EXPECT_TRUE(controller()->IsRevealed());
436
437  // Similar to the TopContainerView, clicking the mouse even slightly off of
438  // the find bar ends the reveal.
439  GetEventGenerator().ClickLeftButton();
440  EXPECT_FALSE(controller()->IsRevealed());
441
442  // Set the find bar bounds to empty. Hovering over the position previously
443  // occupied by the find bar, |over_find_bar|, should end the reveal.
444  controller()->OnFindBarVisibleBoundsChanged(gfx::Rect());
445  AttemptReveal(MODALITY_MOUSE);
446  MoveMouse(over_find_bar.x(), over_find_bar.y());
447  EXPECT_FALSE(controller()->IsRevealed());
448}
449
450// Test revealing the top-of-window views using one modality and ending
451// the reveal via another. For instance, initiating the reveal via a SWIPE_OPEN
452// edge gesture, switching to using the mouse and ending the reveal by moving
453// the mouse off of the top-of-window views.
454TEST_F(ImmersiveModeControllerAshTest, DifferentModalityEnterExit) {
455  controller()->SetEnabled(true);
456  EXPECT_TRUE(controller()->IsEnabled());
457  EXPECT_FALSE(controller()->IsRevealed());
458
459  // Initiate reveal via gesture, end reveal via mouse.
460  AttemptReveal(MODALITY_GESTURE);
461  EXPECT_TRUE(controller()->IsRevealed());
462  MoveMouse(1, 1);
463  EXPECT_TRUE(controller()->IsRevealed());
464  AttemptUnreveal(MODALITY_MOUSE);
465  EXPECT_FALSE(controller()->IsRevealed());
466
467  // Initiate reveal via gesture, end reveal via touch.
468  AttemptReveal(MODALITY_GESTURE);
469  EXPECT_TRUE(controller()->IsRevealed());
470  AttemptUnreveal(MODALITY_TOUCH);
471  EXPECT_FALSE(controller()->IsRevealed());
472
473  // Initiate reveal via mouse, end reveal via gesture.
474  AttemptReveal(MODALITY_MOUSE);
475  EXPECT_TRUE(controller()->IsRevealed());
476  AttemptUnreveal(MODALITY_GESTURE);
477  EXPECT_FALSE(controller()->IsRevealed());
478
479  // Initiate reveal via mouse, end reveal via touch.
480  AttemptReveal(MODALITY_MOUSE);
481  EXPECT_TRUE(controller()->IsRevealed());
482  AttemptUnreveal(MODALITY_TOUCH);
483  EXPECT_FALSE(controller()->IsRevealed());
484}
485
486// Test when the SWIPE_CLOSE edge gesture closes the top-of-window views.
487TEST_F(ImmersiveModeControllerAshTest, EndRevealViaGesture) {
488  controller()->SetEnabled(true);
489  EXPECT_TRUE(controller()->IsEnabled());
490  EXPECT_FALSE(controller()->IsRevealed());
491
492  // A gesture should be able to close the top-of-window views when
493  // top-of-window views have focus.
494  AttemptReveal(MODALITY_MOUSE);
495  top_container()->RequestFocus();
496  EXPECT_TRUE(controller()->IsRevealed());
497  AttemptUnreveal(MODALITY_GESTURE);
498  EXPECT_FALSE(controller()->IsRevealed());
499  top_container()->GetFocusManager()->ClearFocus();
500
501  // If some other code is holding onto a lock, a gesture should not be able to
502  // end the reveal.
503  AttemptReveal(MODALITY_MOUSE);
504  scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock(
505      ImmersiveModeController::ANIMATE_REVEAL_NO));
506  EXPECT_TRUE(controller()->IsRevealed());
507  AttemptUnreveal(MODALITY_GESTURE);
508  EXPECT_TRUE(controller()->IsRevealed());
509  lock.reset();
510  EXPECT_FALSE(controller()->IsRevealed());
511}
512
513// Do not test under windows because focus testing is not reliable on
514// Windows. (crbug.com/79493)
515#if !defined(OS_WIN)
516
517// Test how focus and activation affects whether the top-of-window views are
518// revealed.
519TEST_F(ImmersiveModeControllerAshTest, Focus) {
520  // Add views to the view hierarchy which we will focus and unfocus during the
521  // test.
522  views::View* child_view = new views::View();
523  child_view->SetBounds(0, 0, 10, 10);
524  child_view->set_focusable(true);
525  top_container()->AddChildView(child_view);
526  views::View* unrelated_view = new views::View();
527  unrelated_view->SetBounds(0, 100, 10, 10);
528  unrelated_view->set_focusable(true);
529  top_container()->parent()->AddChildView(unrelated_view);
530  views::FocusManager* focus_manager =
531      top_container()->GetWidget()->GetFocusManager();
532
533  controller()->SetEnabled(true);
534
535  // 1) Test that the top-of-window views stay revealed as long as either a
536  // |child_view| has focus or the mouse is hovered above the top-of-window
537  // views.
538  AttemptReveal(MODALITY_MOUSE);
539  child_view->RequestFocus();
540  focus_manager->ClearFocus();
541  EXPECT_TRUE(controller()->IsRevealed());
542  child_view->RequestFocus();
543  SetHovered(false);
544  EXPECT_TRUE(controller()->IsRevealed());
545  focus_manager->ClearFocus();
546  EXPECT_FALSE(controller()->IsRevealed());
547
548  // 2) Test that focusing |unrelated_view| hides the top-of-window views.
549  // Note: In this test we can cheat and trigger a reveal via focus because
550  // the top container does not hide when the top-of-window views are not
551  // revealed.
552  child_view->RequestFocus();
553  EXPECT_TRUE(controller()->IsRevealed());
554  unrelated_view->RequestFocus();
555  EXPECT_FALSE(controller()->IsRevealed());
556
557  // 3) Test that a loss of focus of |child_view| to |unrelated_view|
558  // while immersive mode is disabled is properly registered.
559  child_view->RequestFocus();
560  EXPECT_TRUE(controller()->IsRevealed());
561  controller()->SetEnabled(false);
562  EXPECT_FALSE(controller()->IsRevealed());
563  unrelated_view->RequestFocus();
564  controller()->SetEnabled(true);
565  EXPECT_FALSE(controller()->IsRevealed());
566
567  // Repeat test but with a revealed lock acquired when immersive mode is
568  // disabled because the code path is different.
569  child_view->RequestFocus();
570  EXPECT_TRUE(controller()->IsRevealed());
571  controller()->SetEnabled(false);
572  scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock(
573      ImmersiveModeController::ANIMATE_REVEAL_NO));
574  EXPECT_FALSE(controller()->IsRevealed());
575  unrelated_view->RequestFocus();
576  controller()->SetEnabled(true);
577  EXPECT_TRUE(controller()->IsRevealed());
578  lock.reset();
579  EXPECT_FALSE(controller()->IsRevealed());
580}
581
582// Test how activation affects whether the top-of-window views are revealed.
583// The behavior when a bubble is activated is tested in
584// ImmersiveModeControllerAshTest.Bubbles.
585TEST_F(ImmersiveModeControllerAshTest, Activation) {
586  views::Widget* top_container_widget = top_container()->GetWidget();
587
588  controller()->SetEnabled(true);
589  ASSERT_FALSE(controller()->IsRevealed());
590
591  // 1) Test that a transient window which is not a bubble does not trigger a
592  // reveal but does keep the top-of-window views revealed if they are already
593  // revealed.
594  views::Widget::InitParams transient_params;
595  transient_params.ownership =
596      views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
597  transient_params.parent = top_container_widget->GetNativeView();
598  transient_params.bounds = gfx::Rect(0, 0, 100, 100);
599  scoped_ptr<views::Widget> transient_widget(new views::Widget());
600  transient_widget->Init(transient_params);
601  transient_widget->Show();
602
603  EXPECT_FALSE(controller()->IsRevealed());
604  top_container_widget->Activate();
605  AttemptReveal(MODALITY_MOUSE);
606  EXPECT_TRUE(controller()->IsRevealed());
607  transient_widget->Activate();
608  SetHovered(false);
609  EXPECT_TRUE(controller()->IsRevealed());
610  transient_widget.reset();
611  EXPECT_FALSE(controller()->IsRevealed());
612
613  // 2) Test that activating a non-transient window ends the reveal if any.
614  views::Widget::InitParams non_transient_params;
615  non_transient_params.ownership =
616      views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
617  non_transient_params.context = top_container_widget->GetNativeView();
618  non_transient_params.bounds = gfx::Rect(0, 0, 100, 100);
619  scoped_ptr<views::Widget> non_transient_widget(new views::Widget());
620  non_transient_widget->Init(non_transient_params);
621  non_transient_widget->Show();
622
623  EXPECT_FALSE(controller()->IsRevealed());
624  top_container_widget->Activate();
625  AttemptReveal(MODALITY_MOUSE);
626  EXPECT_TRUE(controller()->IsRevealed());
627  non_transient_widget->Activate();
628  EXPECT_FALSE(controller()->IsRevealed());
629}
630
631// Test how bubbles affect whether the top-of-window views are revealed.
632TEST_F(ImmersiveModeControllerAshTest, Bubbles) {
633  scoped_ptr<ImmersiveRevealedLock> revealed_lock;
634  views::Widget* top_container_widget = top_container()->GetWidget();
635
636  // Add views to the view hierarchy to which we will anchor bubbles.
637  views::View* child_view = new views::View();
638  child_view->SetBounds(0, 0, 10, 10);
639  top_container()->AddChildView(child_view);
640  views::View* unrelated_view = new views::View();
641  unrelated_view->SetBounds(0, 100, 10, 10);
642  top_container()->parent()->AddChildView(unrelated_view);
643
644  controller()->SetEnabled(true);
645  ASSERT_FALSE(controller()->IsRevealed());
646
647  // 1) Test that a bubble anchored to a child of the top container triggers
648  // a reveal and keeps the top-of-window views revealed for the duration of
649  // its visibility.
650  views::Widget* bubble_widget1(views::BubbleDelegateView::CreateBubble(
651      new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)));
652  bubble_widget1->Show();
653  EXPECT_TRUE(controller()->IsRevealed());
654
655  // Activating |top_container_widget| will close |bubble_widget1|.
656  top_container_widget->Activate();
657  AttemptReveal(MODALITY_MOUSE);
658  revealed_lock.reset(controller()->GetRevealedLock(
659      ImmersiveModeController::ANIMATE_REVEAL_NO));
660  EXPECT_TRUE(controller()->IsRevealed());
661
662  views::Widget* bubble_widget2 = views::BubbleDelegateView::CreateBubble(
663      new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
664  bubble_widget2->Show();
665  EXPECT_TRUE(controller()->IsRevealed());
666  revealed_lock.reset();
667  SetHovered(false);
668  EXPECT_TRUE(controller()->IsRevealed());
669  bubble_widget2->Close();
670  EXPECT_FALSE(controller()->IsRevealed());
671
672  // 2) Test that transitioning from keeping the top-of-window views revealed
673  // because of a bubble to keeping the top-of-window views revealed because of
674  // mouse hover by activating |top_container_widget| works.
675  views::Widget* bubble_widget3 = views::BubbleDelegateView::CreateBubble(
676      new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
677  bubble_widget3->Show();
678  SetHovered(true);
679  EXPECT_TRUE(controller()->IsRevealed());
680
681  SetAnimationsDisabled(false);
682  // Activating |top_container_widget| will close |bubble_widget3|.
683  top_container_widget->Activate();
684  SetAnimationsDisabled(true);
685  EXPECT_TRUE(controller()->IsRevealed());
686
687  // 3) Test that the top-of-window views stay revealed as long as at least one
688  // bubble anchored to a child of the top container is visible.
689  SetHovered(false);
690  EXPECT_FALSE(controller()->IsRevealed());
691
692  views::BubbleDelegateView* bubble_delegate4(new views::BubbleDelegateView(
693      child_view, views::BubbleBorder::NONE));
694  bubble_delegate4->set_use_focusless(true);
695  views::Widget* bubble_widget4(views::BubbleDelegateView::CreateBubble(
696      bubble_delegate4));
697  bubble_widget4->Show();
698
699  views::BubbleDelegateView* bubble_delegate5(new views::BubbleDelegateView(
700      child_view, views::BubbleBorder::NONE));
701  bubble_delegate5->set_use_focusless(true);
702  views::Widget* bubble_widget5(views::BubbleDelegateView::CreateBubble(
703      bubble_delegate5));
704  bubble_widget5->Show();
705
706  EXPECT_TRUE(controller()->IsRevealed());
707  bubble_widget4->Hide();
708  EXPECT_TRUE(controller()->IsRevealed());
709  bubble_widget5->Hide();
710  EXPECT_FALSE(controller()->IsRevealed());
711  bubble_widget5->Show();
712  EXPECT_TRUE(controller()->IsRevealed());
713
714  // 4) Test that visibility changes which occur while immersive fullscreen is
715  // disabled are handled upon reenabling immersive fullscreen.
716  controller()->SetEnabled(false);
717  bubble_widget5->Hide();
718  controller()->SetEnabled(true);
719  EXPECT_FALSE(controller()->IsRevealed());
720
721  // We do not need |bubble_widget4| or |bubble_widget5| anymore, close them.
722  bubble_widget4->Close();
723  bubble_widget5->Close();
724
725  // 5) Test that a bubble added while immersive fullscreen is disabled is
726  // handled upon reenabling immersive fullscreen.
727  controller()->SetEnabled(false);
728
729  views::Widget* bubble_widget6 = views::BubbleDelegateView::CreateBubble(
730      new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
731  bubble_widget6->Show();
732
733  controller()->SetEnabled(true);
734  EXPECT_TRUE(controller()->IsRevealed());
735
736  bubble_widget6->Close();
737
738  // 6) Test that a bubble which is not anchored to a child of the
739  // TopContainerView does not trigger a reveal or keep the
740  // top-of-window views revealed if they are already revealed.
741  views::Widget* bubble_widget7 = views::BubbleDelegateView::CreateBubble(
742      new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE));
743  bubble_widget7->Show();
744  EXPECT_FALSE(controller()->IsRevealed());
745
746  // Activating |top_container_widget| will close |bubble_widget6|.
747  top_container_widget->Activate();
748  AttemptReveal(MODALITY_MOUSE);
749  EXPECT_TRUE(controller()->IsRevealed());
750
751  views::Widget* bubble_widget8 = views::BubbleDelegateView::CreateBubble(
752      new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE));
753  bubble_widget8->Show();
754  SetHovered(false);
755  EXPECT_FALSE(controller()->IsRevealed());
756  bubble_widget8->Close();
757}
758
759#endif  // defined(OS_WIN)
760
761#endif  // defined(OS_CHROMEOS)
762