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