immersive_mode_controller_ash.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
6
7#include "ash/shell.h"
8#include "ash/wm/window_state.h"
9#include "chrome/browser/chrome_notification_types.h"
10#include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
11#include "chrome/browser/ui/views/frame/browser_view.h"
12#include "chrome/browser/ui/views/frame/top_container_view.h"
13#include "chrome/browser/ui/views/tabs/tab_strip.h"
14#include "content/public/browser/notification_service.h"
15#include "content/public/browser/web_contents.h"
16#include "content/public/browser/web_contents_view.h"
17#include "ui/aura/window.h"
18#include "ui/views/view.h"
19#include "ui/views/widget/widget.h"
20#include "ui/views/window/non_client_view.h"
21
22namespace {
23
24// Revealing the TopContainerView looks better if the animation starts and ends
25// just a few pixels before the view goes offscreen, which reduces the visual
26// "pop" as the 3-pixel tall "light bar" style tab strip becomes visible.
27const int kAnimationOffsetY = 3;
28
29// The height of the region in pixels at the top edge of the screen in which to
30// steal touch events targetted at the web contents while in immersive
31// fullscreen. This region is used to allow us to get edge gestures even if the
32// web contents consumes all touch events.
33const int kStealTouchEventsFromWebContentsRegionHeightPx = 8;
34
35// Converts from ImmersiveModeController::AnimateReveal to
36// ash::ImmersiveFullscreenController::AnimateReveal.
37ash::ImmersiveFullscreenController::AnimateReveal
38ToImmersiveFullscreenControllerAnimateReveal(
39    ImmersiveModeController::AnimateReveal animate_reveal) {
40  switch (animate_reveal) {
41    case ImmersiveModeController::ANIMATE_REVEAL_YES:
42      return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_YES;
43    case ImmersiveModeController::ANIMATE_REVEAL_NO:
44      return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
45  }
46  NOTREACHED();
47  return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
48}
49
50}  // namespace
51
52ImmersiveModeControllerAsh::ImmersiveModeControllerAsh()
53    : controller_(new ash::ImmersiveFullscreenController),
54      browser_view_(NULL),
55      native_window_(NULL),
56      observers_enabled_(false),
57      use_tab_indicators_(false),
58      visible_fraction_(1) {
59}
60
61ImmersiveModeControllerAsh::~ImmersiveModeControllerAsh() {
62  EnableWindowObservers(false);
63}
64
65void ImmersiveModeControllerAsh::Init(BrowserView* browser_view) {
66  browser_view_ = browser_view;
67  native_window_ = browser_view_->GetNativeWindow();
68  controller_->Init(this, browser_view_->frame(),
69      browser_view_->top_container());
70}
71
72void ImmersiveModeControllerAsh::SetEnabled(bool enabled) {
73  if (controller_->IsEnabled() == enabled)
74    return;
75
76  EnableWindowObservers(enabled);
77
78  // Use a short "light bar" version of the tab strip when the top-of-window
79  // views are closed. If the user additionally enters into tab fullscreen,
80  // the tab indicators will be hidden.
81  use_tab_indicators_ = enabled;
82
83  controller_->SetEnabled(enabled);
84}
85
86bool ImmersiveModeControllerAsh::IsEnabled() const {
87  return controller_->IsEnabled();
88}
89
90bool ImmersiveModeControllerAsh::ShouldHideTabIndicators() const {
91  return !use_tab_indicators_;
92}
93
94bool ImmersiveModeControllerAsh::ShouldHideTopViews() const {
95  return controller_->IsEnabled() && !controller_->IsRevealed();
96}
97
98bool ImmersiveModeControllerAsh::IsRevealed() const {
99  return controller_->IsRevealed();
100}
101
102int ImmersiveModeControllerAsh::GetTopContainerVerticalOffset(
103    const gfx::Size& top_container_size) const {
104  if (!IsEnabled())
105    return 0;
106
107  // The TopContainerView is flush with the top of |browser_view_| when the
108  // top-of-window views are fully closed so that when the tab indicators are
109  // used, the "light bar" style tab strip is flush with the top of
110  // |browser_view_|.
111  if (!IsRevealed())
112    return 0;
113
114  int height = top_container_size.height() - kAnimationOffsetY;
115  return static_cast<int>(height * (visible_fraction_ - 1));
116}
117
118ImmersiveRevealedLock* ImmersiveModeControllerAsh::GetRevealedLock(
119    AnimateReveal animate_reveal) {
120  return controller_->GetRevealedLock(
121      ToImmersiveFullscreenControllerAnimateReveal(animate_reveal));
122}
123
124void ImmersiveModeControllerAsh::OnFindBarVisibleBoundsChanged(
125    const gfx::Rect& new_visible_bounds_in_screen) {
126  find_bar_visible_bounds_in_screen_ = new_visible_bounds_in_screen;
127}
128
129void ImmersiveModeControllerAsh::SetupForTest() {
130  controller_->SetupForTest();
131}
132
133void ImmersiveModeControllerAsh::EnableWindowObservers(bool enable) {
134  if (observers_enabled_ == enable)
135    return;
136  observers_enabled_ = enable;
137
138  content::Source<FullscreenController> source(
139      browser_view_->browser()->fullscreen_controller());
140  if (enable) {
141    ash::wm::GetWindowState(native_window_)->AddObserver(this);
142    registrar_.Add(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, source);
143  } else {
144    ash::wm::GetWindowState(native_window_)->RemoveObserver(this);
145    registrar_.Remove(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, source);
146  }
147}
148
149void ImmersiveModeControllerAsh::LayoutBrowserRootView() {
150  views::Widget* widget = browser_view_->frame();
151  // Update the window caption buttons.
152  widget->non_client_view()->frame_view()->ResetWindowControls();
153  // Layout all views, including BrowserView.
154  widget->GetRootView()->Layout();
155}
156
157void ImmersiveModeControllerAsh::SetRenderWindowTopInsetsForTouch(
158    int top_inset) {
159  content::WebContents* contents = browser_view_->GetActiveWebContents();
160  if (contents) {
161    aura::Window* window = contents->GetView()->GetContentNativeView();
162    // |window| is NULL if the renderer crashed.
163    if (window) {
164      gfx::Insets inset(top_inset, 0, 0, 0);
165      window->SetHitTestBoundsOverrideOuter(
166          window->hit_test_bounds_override_outer_mouse(),
167          inset);
168    }
169  }
170}
171
172void ImmersiveModeControllerAsh::SetTabIndicatorsVisible(bool visible) {
173  DCHECK(!visible || use_tab_indicators_);
174  if (browser_view_->tabstrip())
175    browser_view_->tabstrip()->SetImmersiveStyle(visible);
176}
177
178void ImmersiveModeControllerAsh::OnImmersiveRevealStarted() {
179  visible_fraction_ = 0;
180  browser_view_->top_container()->SetPaintToLayer(true);
181  SetTabIndicatorsVisible(false);
182  SetRenderWindowTopInsetsForTouch(0);
183  LayoutBrowserRootView();
184  FOR_EACH_OBSERVER(Observer, observers_, OnImmersiveRevealStarted());
185}
186
187void ImmersiveModeControllerAsh::OnImmersiveRevealEnded() {
188  visible_fraction_ = 0;
189  browser_view_->top_container()->SetPaintToLayer(false);
190  SetTabIndicatorsVisible(use_tab_indicators_);
191  SetRenderWindowTopInsetsForTouch(
192      kStealTouchEventsFromWebContentsRegionHeightPx);
193  LayoutBrowserRootView();
194}
195
196void ImmersiveModeControllerAsh::OnImmersiveFullscreenExited() {
197  browser_view_->top_container()->SetPaintToLayer(false);
198  SetTabIndicatorsVisible(false);
199  SetRenderWindowTopInsetsForTouch(0);
200  LayoutBrowserRootView();
201}
202
203void ImmersiveModeControllerAsh::SetVisibleFraction(double visible_fraction) {
204  if (visible_fraction_ != visible_fraction) {
205    visible_fraction_ = visible_fraction;
206    browser_view_->Layout();
207  }
208}
209
210std::vector<gfx::Rect>
211ImmersiveModeControllerAsh::GetVisibleBoundsInScreen() const {
212  views::View* top_container_view = browser_view_->top_container();
213  gfx::Rect top_container_view_bounds = top_container_view->GetVisibleBounds();
214  // TODO(tdanderson): Implement View::ConvertRectToScreen().
215  gfx::Point top_container_view_bounds_in_screen_origin(
216      top_container_view_bounds.origin());
217  views::View::ConvertPointToScreen(top_container_view,
218      &top_container_view_bounds_in_screen_origin);
219  gfx::Rect top_container_view_bounds_in_screen(
220      top_container_view_bounds_in_screen_origin,
221      top_container_view_bounds.size());
222
223  std::vector<gfx::Rect> bounds_in_screen;
224  bounds_in_screen.push_back(top_container_view_bounds_in_screen);
225  bounds_in_screen.push_back(find_bar_visible_bounds_in_screen_);
226  return bounds_in_screen;
227}
228
229void ImmersiveModeControllerAsh::OnWindowShowTypeChanged(
230    ash::wm::WindowState* window_state,
231    ash::wm::WindowShowType old_type) {
232  // Disable immersive fullscreen when the user exits fullscreen without going
233  // through FullscreenController::ToggleFullscreenMode(). This is the case if
234  // the user exits fullscreen via the restore button.
235  if (controller_->IsEnabled() &&
236      !window_state->IsFullscreen() &&
237      !window_state->IsMinimized()) {
238    browser_view_->FullscreenStateChanged();
239  }
240}
241
242void ImmersiveModeControllerAsh::Observe(
243    int type,
244    const content::NotificationSource& source,
245    const content::NotificationDetails& details) {
246  DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type);
247  if (!controller_->IsEnabled())
248    return;
249
250  bool in_tab_fullscreen = content::Source<FullscreenController>(source)->
251      IsFullscreenForTabOrPending();
252
253  bool used_tab_indicators = use_tab_indicators_;
254  use_tab_indicators_ = !in_tab_fullscreen;
255  SetTabIndicatorsVisible(use_tab_indicators_ && !controller_->IsRevealed());
256
257  // Auto hide the shelf in immersive browser fullscreen. When auto hidden, the
258  // shelf displays a 3px 'light bar' when it is closed. When in immersive
259  // browser fullscreen and tab fullscreen, hide the shelf completely and
260  // prevent it from being revealed.
261  ash::wm::GetWindowState(native_window_)->set_hide_shelf_when_fullscreen(
262      in_tab_fullscreen);
263  ash::Shell::GetInstance()->UpdateShelfVisibility();
264
265  if (use_tab_indicators_ != used_tab_indicators)
266    LayoutBrowserRootView();
267}
268