Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / ash / wm / immersive_fullscreen_controller_unittest.cc
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/display/mouse_cursor_event_filter.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shelf/shelf_layout_manager.h"
11 #include "ash/shelf/shelf_types.h"
12 #include "ash/shell.h"
13 #include "ash/test/ash_test_base.h"
14 #include "ash/wm/window_state.h"
15 #include "ui/aura/client/aura_constants.h"
16 #include "ui/aura/client/cursor_client.h"
17 #include "ui/aura/env.h"
18 #include "ui/aura/test/test_window_delegate.h"
19 #include "ui/aura/window.h"
20 #include "ui/aura/window_event_dispatcher.h"
21 #include "ui/events/event_utils.h"
22 #include "ui/events/test/event_generator.h"
23 #include "ui/events/test/test_event_handler.h"
24 #include "ui/gfx/animation/slide_animation.h"
25 #include "ui/views/bubble/bubble_delegate.h"
26 #include "ui/views/controls/native/native_view_host.h"
27 #include "ui/views/view.h"
28 #include "ui/views/widget/widget.h"
29
30 namespace ash {
31
32 namespace {
33
34 class MockImmersiveFullscreenControllerDelegate
35     : public ImmersiveFullscreenController::Delegate {
36  public:
37   MockImmersiveFullscreenControllerDelegate(views::View* top_container_view)
38       : top_container_view_(top_container_view),
39         enabled_(false),
40         visible_fraction_(1) {
41   }
42   ~MockImmersiveFullscreenControllerDelegate() override {}
43
44   // ImmersiveFullscreenController::Delegate overrides:
45   void OnImmersiveRevealStarted() override {
46     enabled_ = true;
47     visible_fraction_ = 0;
48   }
49   void OnImmersiveRevealEnded() override { visible_fraction_ = 0; }
50   void OnImmersiveFullscreenExited() override {
51     enabled_ = false;
52     visible_fraction_ = 1;
53   }
54   void SetVisibleFraction(double visible_fraction) override {
55     visible_fraction_ = visible_fraction;
56   }
57   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;
61   }
62
63   bool is_enabled() const {
64     return enabled_;
65   }
66
67   double visible_fraction() const {
68     return visible_fraction_;
69   }
70
71  private:
72   views::View* top_container_view_;
73   bool enabled_;
74   double visible_fraction_;
75
76   DISALLOW_COPY_AND_ASSIGN(MockImmersiveFullscreenControllerDelegate);
77 };
78
79 class ConsumeEventHandler : public ui::test::TestEventHandler {
80  public:
81   ConsumeEventHandler() {}
82   ~ConsumeEventHandler() override {}
83
84  private:
85   void OnEvent(ui::Event* event) override {
86     ui::test::TestEventHandler::OnEvent(event);
87     if (event->cancelable())
88       event->SetHandled();
89   }
90
91   DISALLOW_COPY_AND_ASSIGN(ConsumeEventHandler);
92 };
93
94 }  // namespace
95
96 /////////////////////////////////////////////////////////////////////////////
97
98 class ImmersiveFullscreenControllerTest : public ash::test::AshTestBase {
99  public:
100   enum Modality {
101     MODALITY_MOUSE,
102     MODALITY_GESTURE_TAP,
103     MODALITY_GESTURE_SCROLL
104   };
105
106   ImmersiveFullscreenControllerTest()
107       : widget_(NULL),
108         top_container_(NULL),
109         content_view_(NULL) {}
110   ~ImmersiveFullscreenControllerTest() override {}
111
112   ImmersiveFullscreenController* controller() {
113     return controller_.get();
114   }
115
116   views::NativeViewHost* content_view() {
117     return content_view_;
118   }
119
120   views::View* top_container() {
121     return top_container_;
122   }
123
124   views::Widget* widget() { return widget_; }
125
126   aura::Window* window() {
127     return widget_->GetNativeWindow();
128   }
129
130   MockImmersiveFullscreenControllerDelegate* delegate() {
131     return delegate_.get();
132   }
133
134   // Access to private data from the controller.
135   bool top_edge_hover_timer_running() const {
136     return controller_->top_edge_hover_timer_.IsRunning();
137   }
138   int mouse_x_when_hit_top() const {
139     return controller_->mouse_x_when_hit_top_in_screen_;
140   }
141
142   // ash::test::AshTestBase overrides:
143   void SetUp() override {
144     ash::test::AshTestBase::SetUp();
145
146     widget_ = new views::Widget();
147     views::Widget::InitParams params;
148     params.context = CurrentContext();
149     widget_->Init(params);
150     widget_->Show();
151
152     window()->SetProperty(aura::client::kShowStateKey,
153                           ui::SHOW_STATE_FULLSCREEN);
154
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_);
159
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_);
165
166     delegate_.reset(
167         new MockImmersiveFullscreenControllerDelegate(top_container_));
168     controller_.reset(new ImmersiveFullscreenController);
169     controller_->Init(delegate_.get(), widget_, top_container_);
170     controller_->SetupForTest();
171
172     // The mouse is moved so that it is not over |top_container_| by
173     // AshTestBase.
174   }
175
176   // Enables / disables immersive fullscreen.
177   void SetEnabled(bool enabled) {
178     controller_->SetEnabled(ImmersiveFullscreenController::WINDOW_TYPE_OTHER,
179                             enabled);
180   }
181
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);
187   }
188
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);
193   }
194
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);
200   }
201
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());
208
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
212     // the screen.
213     if (controller()->top_edge_hover_timer_.IsRunning()) {
214       controller()->top_edge_hover_timer_.user_task().Run();
215       controller()->top_edge_hover_timer_.Stop();
216     }
217   }
218
219  private:
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);
224     switch (modality) {
225       case MODALITY_MOUSE: {
226         MoveMouse(event_position.x(), event_position.y());
227         break;
228       }
229       case MODALITY_GESTURE_TAP: {
230         gfx::Point screen_position = event_position;
231         views::View::ConvertPointToScreen(top_container_, &screen_position);
232         ui::test::EventGenerator& event_generator(GetEventGenerator());
233         event_generator.MoveTouch(event_position);
234         event_generator.PressTouch();
235         event_generator.ReleaseTouch();
236         break;
237       }
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         ui::test::EventGenerator& event_generator(GetEventGenerator());
245         event_generator.GestureScrollSequence(
246             start, end,
247             base::TimeDelta::FromMilliseconds(30), 1);
248         break;
249       }
250     }
251   }
252
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.
258
259   DISALLOW_COPY_AND_ASSIGN(ImmersiveFullscreenControllerTest);
260 };
261
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) {
265   // Initial state.
266   EXPECT_FALSE(controller()->IsEnabled());
267   EXPECT_FALSE(controller()->IsRevealed());
268   EXPECT_FALSE(delegate()->is_enabled());
269
270   // Enabling initially hides the top views.
271   SetEnabled(true);
272   EXPECT_TRUE(controller()->IsEnabled());
273   EXPECT_FALSE(controller()->IsRevealed());
274   EXPECT_TRUE(delegate()->is_enabled());
275   EXPECT_EQ(0, delegate()->visible_fraction());
276
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());
283
284   // Disabling ends the immersive reveal.
285   SetEnabled(false);
286   EXPECT_FALSE(controller()->IsEnabled());
287   EXPECT_FALSE(controller()->IsRevealed());
288   EXPECT_FALSE(delegate()->is_enabled());
289 }
290
291 // GetRevealedLock() specific tests.
292 TEST_F(ImmersiveFullscreenControllerTest, RevealedLock) {
293   scoped_ptr<ImmersiveRevealedLock> lock1;
294   scoped_ptr<ImmersiveRevealedLock> lock2;
295
296   // Immersive fullscreen is not on by default.
297   EXPECT_FALSE(controller()->IsEnabled());
298
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());
306
307   // Immersive fullscreen should start in the revealed state due to the lock.
308   SetEnabled(true);
309   EXPECT_TRUE(controller()->IsEnabled());
310   EXPECT_TRUE(controller()->IsRevealed());
311
312   SetEnabled(false);
313   EXPECT_FALSE(controller()->IsEnabled());
314   EXPECT_FALSE(controller()->IsRevealed());
315
316   lock1.reset();
317   EXPECT_FALSE(controller()->IsEnabled());
318   EXPECT_FALSE(controller()->IsRevealed());
319
320   // Immersive fullscreen should start in the closed state because the lock is
321   // no longer held.
322   SetEnabled(true);
323   EXPECT_TRUE(controller()->IsEnabled());
324   EXPECT_FALSE(controller()->IsRevealed());
325
326   // 2) Test that acquiring a lock reveals the top-of-window views if they are
327   // hidden.
328   lock1.reset(controller()->GetRevealedLock(
329       ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
330   EXPECT_TRUE(controller()->IsRevealed());
331
332   // 3) Test that the top-of-window views are only hidden when all of the locks
333   // are released.
334   lock2.reset(controller()->GetRevealedLock(
335       ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
336   lock1.reset();
337   EXPECT_TRUE(controller()->IsRevealed());
338
339   lock2.reset();
340   EXPECT_FALSE(controller()->IsRevealed());
341 }
342
343 // Test mouse event processing for top-of-screen reveal triggering.
344 TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) {
345   // Set up initial state.
346   SetEnabled(true);
347   ASSERT_TRUE(controller()->IsEnabled());
348   ASSERT_FALSE(controller()->IsRevealed());
349
350   ui::test::EventGenerator& event_generator(GetEventGenerator());
351
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());
357
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());
363
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());
369
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());
375
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());
380
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());
385
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());
390
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());
396
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());
401
402   // Once revealed, a move just a little below the top container doesn't end a
403   // reveal.
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());
408
409   // Once revealed, clicking just below the top container ends the reveal.
410   event_generator.ClickLeftButton();
411   EXPECT_FALSE(controller()->IsRevealed());
412
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());
419
420   // The mouse position cannot cause a reveal when the top container's widget
421   // has capture.
422   views::Widget* widget = top_container()->GetWidget();
423   widget->SetCapture(top_container());
424   AttemptReveal(MODALITY_MOUSE);
425   EXPECT_FALSE(controller()->IsRevealed());
426   widget->ReleaseCapture();
427
428   // The mouse position cannot end the reveal while the top container's widget
429   // has capture.
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());
436
437   // Releasing capture should end the reveal.
438   widget->ReleaseCapture();
439   EXPECT_FALSE(controller()->IsRevealed());
440 }
441
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(
447       NULL,
448       CurrentContext(),
449       gfx::Rect(0, 0, 200, 200));
450   popup_widget->Show();
451   ASSERT_FALSE(top_container()->GetWidget()->IsActive());
452
453   SetEnabled(true);
454   ASSERT_TRUE(controller()->IsEnabled());
455   ASSERT_FALSE(controller()->IsRevealed());
456
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());
464
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());
470
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());
476
477   // The top-of-window views should stay revealed if the cursor is moved off
478   // of the top edge.
479   MoveMouse(top_container_bounds_in_screen.right() - 1,
480             top_container_bounds_in_screen.bottom() - 1);
481   EXPECT_TRUE(controller()->IsRevealed());
482
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());
487
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());
498 }
499
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())
505     return;
506
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(
511       display_layout);
512
513   SetEnabled(true);
514   ASSERT_TRUE(controller()->IsEnabled());
515   ASSERT_FALSE(controller()->IsRevealed());
516
517   aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
518   ASSERT_EQ(root_windows[0],
519             top_container()->GetWidget()->GetNativeWindow()->GetRootWindow());
520
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();
528
529   ui::test::EventGenerator& event_generator(GetEventGenerator());
530
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
533   // running.
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());
538
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
544   // The timer should continue running if the user overshoots the top edge
545   // a bit.
546   event_generator.MoveMouseTo(x, y_top_edge - 2);
547   EXPECT_TRUE(top_edge_hover_timer_running());
548
549   // The timer should stop running if the user overshoots the top edge by
550   // a lot.
551   event_generator.MoveMouseTo(x, y_top_edge - 20);
552   EXPECT_FALSE(top_edge_hover_timer_running());
553
554   // The timer should not start if the user moves the mouse to the bottom of the
555   // secondary display without crossing the top edge first.
556   event_generator.MoveMouseTo(x, y_top_edge - 2);
557
558   // Reveal the top-of-window views by overshooting the top edge slightly.
559   event_generator.MoveMouseTo(x, y_top_edge + 1);
560   // MoveMouse() runs the timer task.
561   MoveMouse(x, y_top_edge - 2);
562   EXPECT_TRUE(controller()->IsRevealed());
563
564   // The top-of-window views should stay revealed if the user moves the mouse
565   // around in the bottom region of the secondary display.
566   event_generator.MoveMouseTo(x + 10, y_top_edge - 3);
567   EXPECT_TRUE(controller()->IsRevealed());
568
569   // The top-of-window views should hide if the user moves the mouse away from
570   // the bottom region of the secondary display.
571   event_generator.MoveMouseTo(x, y_top_edge - 20);
572   EXPECT_FALSE(controller()->IsRevealed());
573
574   // Test that it is possible to reveal the top-of-window views by overshooting
575   // the top edge slightly when the top container's widget is not active.
576   views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds(
577       NULL,
578       CurrentContext(),
579       gfx::Rect(0, 200, 100, 100));
580   popup_widget->Show();
581   ASSERT_FALSE(top_container()->GetWidget()->IsActive());
582   ASSERT_FALSE(top_container()->GetBoundsInScreen().Intersects(
583       popup_widget->GetWindowBoundsInScreen()));
584   event_generator.MoveMouseTo(x, y_top_edge + 1);
585   MoveMouse(x, y_top_edge - 2);
586   EXPECT_TRUE(controller()->IsRevealed());
587 }
588
589 // Test behavior when the mouse becomes hovered without moving.
590 TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) {
591   SetEnabled(true);
592   scoped_ptr<ImmersiveRevealedLock> lock;
593
594   // 1) Test that if the mouse becomes hovered without the mouse moving due to a
595   // lock causing the top-of-window views to be revealed (and the mouse
596   // happening to be near the top of the screen), the top-of-window views do not
597   // hide till the mouse moves off of the top-of-window views.
598   SetHovered(true);
599   EXPECT_FALSE(controller()->IsRevealed());
600   lock.reset(controller()->GetRevealedLock(
601       ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
602   EXPECT_TRUE(controller()->IsRevealed());
603   lock.reset();
604   EXPECT_TRUE(controller()->IsRevealed());
605   SetHovered(false);
606   EXPECT_FALSE(controller()->IsRevealed());
607
608   // 2) Test that if the mouse becomes hovered without moving because of a
609   // reveal in ImmersiveFullscreenController::SetEnabled(true) and there are no
610   // locks keeping the top-of-window views revealed, that mouse hover does not
611   // prevent the top-of-window views from closing.
612   SetEnabled(false);
613   SetHovered(true);
614   EXPECT_FALSE(controller()->IsRevealed());
615   SetEnabled(true);
616   EXPECT_FALSE(controller()->IsRevealed());
617
618   // 3) Test that if the mouse becomes hovered without moving because of a
619   // reveal in ImmersiveFullscreenController::SetEnabled(true) and there is a
620   // lock keeping the top-of-window views revealed, that the top-of-window views
621   // do not hide till the mouse moves off of the top-of-window views.
622   SetEnabled(false);
623   SetHovered(true);
624   lock.reset(controller()->GetRevealedLock(
625       ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
626   EXPECT_FALSE(controller()->IsRevealed());
627   SetEnabled(true);
628   EXPECT_TRUE(controller()->IsRevealed());
629   lock.reset();
630   EXPECT_TRUE(controller()->IsRevealed());
631   SetHovered(false);
632   EXPECT_FALSE(controller()->IsRevealed());
633 }
634
635 // Test revealing the top-of-window views using one modality and ending
636 // the reveal via another. For instance, initiating the reveal via a SWIPE_OPEN
637 // edge gesture, switching to using the mouse and ending the reveal by moving
638 // the mouse off of the top-of-window views.
639 TEST_F(ImmersiveFullscreenControllerTest, DifferentModalityEnterExit) {
640   SetEnabled(true);
641   EXPECT_TRUE(controller()->IsEnabled());
642   EXPECT_FALSE(controller()->IsRevealed());
643
644   // Initiate reveal via gesture, end reveal via mouse.
645   AttemptReveal(MODALITY_GESTURE_SCROLL);
646   EXPECT_TRUE(controller()->IsRevealed());
647   MoveMouse(1, 1);
648   EXPECT_TRUE(controller()->IsRevealed());
649   AttemptUnreveal(MODALITY_MOUSE);
650   EXPECT_FALSE(controller()->IsRevealed());
651
652   // Initiate reveal via gesture, end reveal via touch.
653   AttemptReveal(MODALITY_GESTURE_SCROLL);
654   EXPECT_TRUE(controller()->IsRevealed());
655   AttemptUnreveal(MODALITY_GESTURE_TAP);
656   EXPECT_FALSE(controller()->IsRevealed());
657
658   // Initiate reveal via mouse, end reveal via gesture.
659   AttemptReveal(MODALITY_MOUSE);
660   EXPECT_TRUE(controller()->IsRevealed());
661   AttemptUnreveal(MODALITY_GESTURE_SCROLL);
662   EXPECT_FALSE(controller()->IsRevealed());
663
664   // Initiate reveal via mouse, end reveal via touch.
665   AttemptReveal(MODALITY_MOUSE);
666   EXPECT_TRUE(controller()->IsRevealed());
667   AttemptUnreveal(MODALITY_GESTURE_TAP);
668   EXPECT_FALSE(controller()->IsRevealed());
669 }
670
671 // Test when the SWIPE_CLOSE edge gesture closes the top-of-window views.
672 #if defined(OS_WIN)
673 // On Windows, touch events do not result in mouse events being disabled.  As
674 // a result, the last part of this test which ends the reveal via a gesture will
675 // not work correctly.  See crbug.com/332430, and the function
676 // ShouldHideCursorOnTouch() in compound_event_filter.cc.
677 #define MAYBE_EndRevealViaGesture DISABLED_EndRevealViaGesture
678 #else
679 #define MAYBE_EndRevealViaGesture EndRevealViaGesture
680 #endif
681 TEST_F(ImmersiveFullscreenControllerTest, MAYBE_EndRevealViaGesture) {
682   SetEnabled(true);
683   EXPECT_TRUE(controller()->IsEnabled());
684   EXPECT_FALSE(controller()->IsRevealed());
685
686   // A gesture should be able to close the top-of-window views when
687   // top-of-window views have focus.
688   AttemptReveal(MODALITY_MOUSE);
689   top_container()->RequestFocus();
690   EXPECT_TRUE(controller()->IsRevealed());
691   AttemptUnreveal(MODALITY_GESTURE_SCROLL);
692   EXPECT_FALSE(controller()->IsRevealed());
693
694   // The top-of-window views should no longer have focus. Clearing focus is
695   // important because it closes focus-related popup windows like the touch
696   // selection handles.
697   EXPECT_FALSE(top_container()->HasFocus());
698
699   // If some other code is holding onto a lock, a gesture should not be able to
700   // end the reveal.
701   AttemptReveal(MODALITY_MOUSE);
702   scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock(
703       ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
704   EXPECT_TRUE(controller()->IsRevealed());
705   AttemptUnreveal(MODALITY_GESTURE_SCROLL);
706   EXPECT_TRUE(controller()->IsRevealed());
707   lock.reset();
708   EXPECT_FALSE(controller()->IsRevealed());
709 }
710
711 // Tests that touch-gesture can be used to reveal the top-of-window views when
712 // the child window consumes all events.
713 TEST_F(ImmersiveFullscreenControllerTest, RevealViaGestureChildConsumesEvents) {
714   // Enabling initially hides the top views.
715   SetEnabled(true);
716   EXPECT_TRUE(controller()->IsEnabled());
717   EXPECT_FALSE(controller()->IsRevealed());
718
719   aura::test::TestWindowDelegate child_delegate;
720   scoped_ptr<aura::Window> child(
721       CreateTestWindowInShellWithDelegateAndType(&child_delegate,
722                                                  ui::wm::WINDOW_TYPE_CONTROL,
723                                                  1234,
724                                                  gfx::Rect()));
725   content_view()->Attach(child.get());
726   child->Show();
727
728   ConsumeEventHandler handler;
729   child->AddPreTargetHandler(&handler);
730
731   // Reveal the top views using a touch-scroll gesture. The child window should
732   // not receive the touch events.
733   AttemptReveal(MODALITY_GESTURE_SCROLL);
734   EXPECT_TRUE(controller()->IsRevealed());
735   EXPECT_EQ(0, handler.num_touch_events());
736
737   AttemptUnreveal(MODALITY_GESTURE_TAP);
738   EXPECT_FALSE(controller()->IsRevealed());
739   EXPECT_GT(handler.num_touch_events(), 0);
740   child->RemovePreTargetHandler(&handler);
741 }
742
743 // Make sure touch events towards the top of the window do not leak through to
744 // windows underneath.
745 TEST_F(ImmersiveFullscreenControllerTest, EventsDoNotLeakToWindowUnderneath) {
746   gfx::Rect window_bounds = window()->GetBoundsInScreen();
747   aura::test::TestWindowDelegate child_delegate;
748   scoped_ptr<aura::Window> behind(CreateTestWindowInShellWithDelegate(
749       &child_delegate, 1234, window_bounds));
750   behind->Show();
751   behind->SetBounds(window_bounds);
752   widget()->StackAbove(behind.get());
753
754   // Make sure the windows are aligned on top.
755   EXPECT_EQ(behind->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
756   int top = behind->GetBoundsInScreen().y();
757
758   ui::TouchEvent touch(ui::ET_TOUCH_MOVED, gfx::Point(10, top), 0,
759                        ui::EventTimeForNow());
760   ui::EventTarget* root = window()->GetRootWindow();
761   ui::EventTargeter* targeter = root->GetEventTargeter();
762   EXPECT_EQ(window(), targeter->FindTargetForEvent(root, &touch));
763
764   SetEnabled(true);
765   EXPECT_FALSE(controller()->IsRevealed());
766   // Make sure the windows are still aligned on top.
767   EXPECT_EQ(behind->GetBoundsInScreen().y(), window()->GetBoundsInScreen().y());
768   top = behind->GetBoundsInScreen().y();
769   ui::TouchEvent touch2(ui::ET_TOUCH_MOVED, gfx::Point(10, top), 0,
770                         ui::EventTimeForNow());
771   // The event should still be targeted to window().
772   EXPECT_EQ(window(), targeter->FindTargetForEvent(root, &touch2));
773 }
774
775 // Check that the window state gets properly marked for immersive fullscreen.
776 TEST_F(ImmersiveFullscreenControllerTest, WindowStateImmersiveFullscreen) {
777   ash::wm::WindowState* window_state = ash::wm::GetWindowState(window());
778
779   EXPECT_FALSE(window_state->in_immersive_fullscreen());
780   SetEnabled(true);
781   ASSERT_TRUE(controller()->IsEnabled());
782   EXPECT_TRUE(window_state->in_immersive_fullscreen());
783
784   SetEnabled(false);
785   ASSERT_FALSE(controller()->IsEnabled());
786   EXPECT_FALSE(window_state->in_immersive_fullscreen());
787 }
788
789 // Do not test under windows because focus testing is not reliable on
790 // Windows. (crbug.com/79493)
791 #if !defined(OS_WIN)
792
793 // Test how focus and activation affects whether the top-of-window views are
794 // revealed.
795 TEST_F(ImmersiveFullscreenControllerTest, Focus) {
796   // Add views to the view hierarchy which we will focus and unfocus during the
797   // test.
798   views::View* child_view = new views::View();
799   child_view->SetBounds(0, 0, 10, 10);
800   child_view->SetFocusable(true);
801   top_container()->AddChildView(child_view);
802   views::View* unrelated_view = new views::View();
803   unrelated_view->SetBounds(0, 100, 10, 10);
804   unrelated_view->SetFocusable(true);
805   top_container()->parent()->AddChildView(unrelated_view);
806   views::FocusManager* focus_manager =
807       top_container()->GetWidget()->GetFocusManager();
808
809   SetEnabled(true);
810
811   // 1) Test that the top-of-window views stay revealed as long as either a
812   // |child_view| has focus or the mouse is hovered above the top-of-window
813   // views.
814   AttemptReveal(MODALITY_MOUSE);
815   child_view->RequestFocus();
816   focus_manager->ClearFocus();
817   EXPECT_TRUE(controller()->IsRevealed());
818   child_view->RequestFocus();
819   SetHovered(false);
820   EXPECT_TRUE(controller()->IsRevealed());
821   focus_manager->ClearFocus();
822   EXPECT_FALSE(controller()->IsRevealed());
823
824   // 2) Test that focusing |unrelated_view| hides the top-of-window views.
825   // Note: In this test we can cheat and trigger a reveal via focus because
826   // the top container does not hide when the top-of-window views are not
827   // revealed.
828   child_view->RequestFocus();
829   EXPECT_TRUE(controller()->IsRevealed());
830   unrelated_view->RequestFocus();
831   EXPECT_FALSE(controller()->IsRevealed());
832
833   // 3) Test that a loss of focus of |child_view| to |unrelated_view|
834   // while immersive mode is disabled is properly registered.
835   child_view->RequestFocus();
836   EXPECT_TRUE(controller()->IsRevealed());
837   SetEnabled(false);
838   EXPECT_FALSE(controller()->IsRevealed());
839   unrelated_view->RequestFocus();
840   SetEnabled(true);
841   EXPECT_FALSE(controller()->IsRevealed());
842
843   // Repeat test but with a revealed lock acquired when immersive mode is
844   // disabled because the code path is different.
845   child_view->RequestFocus();
846   EXPECT_TRUE(controller()->IsRevealed());
847   SetEnabled(false);
848   scoped_ptr<ImmersiveRevealedLock> lock(controller()->GetRevealedLock(
849       ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
850   EXPECT_FALSE(controller()->IsRevealed());
851   unrelated_view->RequestFocus();
852   SetEnabled(true);
853   EXPECT_TRUE(controller()->IsRevealed());
854   lock.reset();
855   EXPECT_FALSE(controller()->IsRevealed());
856 }
857
858 // Test how transient windows affect whether the top-of-window views are
859 // revealed.
860 TEST_F(ImmersiveFullscreenControllerTest, Transient) {
861   views::Widget* top_container_widget = top_container()->GetWidget();
862
863   SetEnabled(true);
864   ASSERT_FALSE(controller()->IsRevealed());
865
866   // 1) Test that a transient window which is not a bubble does not trigger a
867   // reveal but does keep the top-of-window views revealed if they are already
868   // revealed.
869   views::Widget::InitParams transient_params;
870   transient_params.ownership =
871       views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
872   transient_params.parent = top_container_widget->GetNativeView();
873   transient_params.bounds = gfx::Rect(0, 100, 100, 100);
874   scoped_ptr<views::Widget> transient_widget(new views::Widget());
875   transient_widget->Init(transient_params);
876
877   EXPECT_FALSE(controller()->IsRevealed());
878   AttemptReveal(MODALITY_MOUSE);
879   EXPECT_TRUE(controller()->IsRevealed());
880   transient_widget->Show();
881   SetHovered(false);
882   EXPECT_TRUE(controller()->IsRevealed());
883   transient_widget.reset();
884   EXPECT_FALSE(controller()->IsRevealed());
885
886   // 2) Test that activating a non-transient window does not keep the
887   // top-of-window views revealed.
888   views::Widget::InitParams non_transient_params;
889   non_transient_params.ownership =
890       views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
891   non_transient_params.context = top_container_widget->GetNativeView();
892   non_transient_params.bounds = gfx::Rect(0, 100, 100, 100);
893   scoped_ptr<views::Widget> non_transient_widget(new views::Widget());
894   non_transient_widget->Init(non_transient_params);
895
896   EXPECT_FALSE(controller()->IsRevealed());
897   AttemptReveal(MODALITY_MOUSE);
898   EXPECT_TRUE(controller()->IsRevealed());
899   non_transient_widget->Show();
900   SetHovered(false);
901   EXPECT_FALSE(controller()->IsRevealed());
902 }
903
904 // Test how bubbles affect whether the top-of-window views are revealed.
905 TEST_F(ImmersiveFullscreenControllerTest, Bubbles) {
906   scoped_ptr<ImmersiveRevealedLock> revealed_lock;
907   views::Widget* top_container_widget = top_container()->GetWidget();
908
909   // Add views to the view hierarchy to which we will anchor bubbles.
910   views::View* child_view = new views::View();
911   child_view->SetBounds(0, 0, 10, 10);
912   top_container()->AddChildView(child_view);
913   views::View* unrelated_view = new views::View();
914   unrelated_view->SetBounds(0, 100, 10, 10);
915   top_container()->parent()->AddChildView(unrelated_view);
916
917   SetEnabled(true);
918   ASSERT_FALSE(controller()->IsRevealed());
919
920   // 1) Test that a bubble anchored to a child of the top container triggers
921   // a reveal and keeps the top-of-window views revealed for the duration of
922   // its visibility.
923   views::Widget* bubble_widget1(views::BubbleDelegateView::CreateBubble(
924       new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE)));
925   bubble_widget1->Show();
926   EXPECT_TRUE(controller()->IsRevealed());
927
928   // Activating |top_container_widget| will close |bubble_widget1|.
929   top_container_widget->Activate();
930   AttemptReveal(MODALITY_MOUSE);
931   revealed_lock.reset(controller()->GetRevealedLock(
932       ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
933   EXPECT_TRUE(controller()->IsRevealed());
934
935   views::Widget* bubble_widget2 = views::BubbleDelegateView::CreateBubble(
936       new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
937   bubble_widget2->Show();
938   EXPECT_TRUE(controller()->IsRevealed());
939   revealed_lock.reset();
940   SetHovered(false);
941   EXPECT_TRUE(controller()->IsRevealed());
942   bubble_widget2->Close();
943   EXPECT_FALSE(controller()->IsRevealed());
944
945   // 2) Test that transitioning from keeping the top-of-window views revealed
946   // because of a bubble to keeping the top-of-window views revealed because of
947   // mouse hover by activating |top_container_widget| works.
948   views::Widget* bubble_widget3 = views::BubbleDelegateView::CreateBubble(
949       new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
950   bubble_widget3->Show();
951   SetHovered(true);
952   EXPECT_TRUE(controller()->IsRevealed());
953   top_container_widget->Activate();
954   EXPECT_TRUE(controller()->IsRevealed());
955
956   // 3) Test that the top-of-window views stay revealed as long as at least one
957   // bubble anchored to a child of the top container is visible.
958   SetHovered(false);
959   EXPECT_FALSE(controller()->IsRevealed());
960
961   views::BubbleDelegateView* bubble_delegate4(new views::BubbleDelegateView(
962       child_view, views::BubbleBorder::NONE));
963   bubble_delegate4->set_can_activate(false);
964   views::Widget* bubble_widget4(views::BubbleDelegateView::CreateBubble(
965       bubble_delegate4));
966   bubble_widget4->Show();
967
968   views::BubbleDelegateView* bubble_delegate5(new views::BubbleDelegateView(
969       child_view, views::BubbleBorder::NONE));
970   bubble_delegate5->set_can_activate(false);
971   views::Widget* bubble_widget5(views::BubbleDelegateView::CreateBubble(
972       bubble_delegate5));
973   bubble_widget5->Show();
974
975   EXPECT_TRUE(controller()->IsRevealed());
976   bubble_widget4->Hide();
977   EXPECT_TRUE(controller()->IsRevealed());
978   bubble_widget5->Hide();
979   EXPECT_FALSE(controller()->IsRevealed());
980   bubble_widget5->Show();
981   EXPECT_TRUE(controller()->IsRevealed());
982
983   // 4) Test that visibility changes which occur while immersive fullscreen is
984   // disabled are handled upon reenabling immersive fullscreen.
985   SetEnabled(false);
986   bubble_widget5->Hide();
987   SetEnabled(true);
988   EXPECT_FALSE(controller()->IsRevealed());
989
990   // We do not need |bubble_widget4| or |bubble_widget5| anymore, close them.
991   bubble_widget4->Close();
992   bubble_widget5->Close();
993
994   // 5) Test that a bubble added while immersive fullscreen is disabled is
995   // handled upon reenabling immersive fullscreen.
996   SetEnabled(false);
997
998   views::Widget* bubble_widget6 = views::BubbleDelegateView::CreateBubble(
999       new views::BubbleDelegateView(child_view, views::BubbleBorder::NONE));
1000   bubble_widget6->Show();
1001
1002   SetEnabled(true);
1003   EXPECT_TRUE(controller()->IsRevealed());
1004
1005   bubble_widget6->Close();
1006
1007   // 6) Test that a bubble which is not anchored to a child of the
1008   // TopContainerView does not trigger a reveal or keep the
1009   // top-of-window views revealed if they are already revealed.
1010   views::Widget* bubble_widget7 = views::BubbleDelegateView::CreateBubble(
1011       new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE));
1012   bubble_widget7->Show();
1013   EXPECT_FALSE(controller()->IsRevealed());
1014
1015   // Activating |top_container_widget| will close |bubble_widget6|.
1016   top_container_widget->Activate();
1017   AttemptReveal(MODALITY_MOUSE);
1018   EXPECT_TRUE(controller()->IsRevealed());
1019
1020   views::Widget* bubble_widget8 = views::BubbleDelegateView::CreateBubble(
1021       new views::BubbleDelegateView(unrelated_view, views::BubbleBorder::NONE));
1022   bubble_widget8->Show();
1023   SetHovered(false);
1024   EXPECT_FALSE(controller()->IsRevealed());
1025   bubble_widget8->Close();
1026 }
1027
1028 #endif  // defined(OS_WIN)
1029
1030 // Test that the shelf is set to auto hide as long as the window is in
1031 // immersive fullscreen and that the shelf's state before entering immersive
1032 // fullscreen is restored upon exiting immersive fullscreen.
1033 TEST_F(ImmersiveFullscreenControllerTest, Shelf) {
1034   ash::ShelfLayoutManager* shelf =
1035       ash::Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
1036
1037   // Shelf is visible by default.
1038   window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1039   ASSERT_FALSE(controller()->IsEnabled());
1040   ASSERT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state());
1041
1042   // Entering immersive fullscreen sets the shelf to auto hide.
1043   window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
1044   SetEnabled(true);
1045   EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1046
1047   // Disabling immersive fullscreen puts it back.
1048   SetEnabled(false);
1049   window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1050   ASSERT_FALSE(controller()->IsEnabled());
1051   EXPECT_EQ(ash::SHELF_VISIBLE, shelf->visibility_state());
1052
1053   // The user could toggle the shelf auto-hide behavior.
1054   shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
1055   EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1056
1057   // Entering immersive fullscreen keeps auto-hide.
1058   window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
1059   SetEnabled(true);
1060   EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1061
1062   // Disabling immersive fullscreen maintains the user's auto-hide selection.
1063   SetEnabled(false);
1064   window()->SetProperty(aura::client::kShowStateKey,
1065                         ui::SHOW_STATE_NORMAL);
1066   EXPECT_EQ(ash::SHELF_AUTO_HIDE, shelf->visibility_state());
1067 }
1068
1069 }  // namespase ash