Upstream version 5.34.104.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/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
31 namespace ash {
32
33 namespace {
34
35 class 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
82 class 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
101 class 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.
267 TEST_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.
295 TEST_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.
347 TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) {
348   // Set up initial state.
349   UpdateDisplay("800x600,800x600");
350   ash::DisplayLayout display_layout(ash::DisplayLayout::RIGHT, 0);
351   ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays(
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.
452 TEST_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.
511 TEST_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.
600 TEST_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.
649 TEST_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.
682 TEST_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.
714 TEST_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.
746 TEST_F(ImmersiveFullscreenControllerTest, EventsDoNotLeakToWindowUnderneath) {
747   gfx::Rect window_bounds = window()->GetBoundsInScreen();
748   aura::test::TestWindowDelegate child_delegate;
749   scoped_ptr<aura::Window> behind(CreateTestWindowInShellWithDelegate(
750       &child_delegate, 1234, window_bounds));
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.
782 TEST_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.
847 TEST_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.
892 TEST_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.
1020 TEST_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)