Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc

Issue 48963002: [Refactor] Move the non-browser specific logic of ImmersiveModeControllerAsh into ash part 2 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h" 5 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
6 6
7 #include <set>
8 #include <vector>
9
10 #include "ash/shell.h" 7 #include "ash/shell.h"
11 #include "ash/wm/window_state.h" 8 #include "ash/wm/window_state.h"
12 #include "chrome/browser/chrome_notification_types.h" 9 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" 10 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
14 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h" 11 #include "chrome/browser/ui/views/frame/browser_view.h"
15 #include "chrome/browser/ui/views/frame/top_container_view.h" 12 #include "chrome/browser/ui/views/frame/top_container_view.h"
13 #include "chrome/browser/ui/views/tabs/tab_strip.h"
16 #include "content/public/browser/notification_service.h" 14 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/web_contents.h" 15 #include "content/public/browser/web_contents.h"
18 #include "content/public/browser/web_contents_view.h" 16 #include "content/public/browser/web_contents_view.h"
19 #include "ui/aura/client/activation_client.h"
20 #include "ui/aura/client/aura_constants.h"
21 #include "ui/aura/client/capture_client.h"
22 #include "ui/aura/client/cursor_client.h"
23 #include "ui/aura/client/screen_position_client.h"
24 #include "ui/aura/env.h"
25 #include "ui/aura/root_window.h"
26 #include "ui/aura/window.h" 17 #include "ui/aura/window.h"
27 #include "ui/gfx/animation/slide_animation.h"
28 #include "ui/views/bubble/bubble_delegate.h"
29 #include "ui/views/view.h" 18 #include "ui/views/view.h"
30 #include "ui/views/widget/widget.h" 19 #include "ui/views/widget/widget.h"
31 #include "ui/views/window/non_client_view.h" 20 #include "ui/views/window/non_client_view.h"
32 21
33 using views::View;
34
35 namespace { 22 namespace {
36 23
37 // The slide open/closed animation looks better if it starts and ends just a 24 // Revealing the TopContainerView looks better if the animation starts and ends
38 // few pixels before the view goes completely off the screen, which reduces 25 // just a few pixels before the view goes offscreen, which reduces the visual
39 // the visual "pop" as the 2-pixel tall immersive-style tabs become visible. 26 // "pop" as the 3-pixel tall "light bar" style tab strip becomes visible.
40 const int kAnimationOffsetY = 3; 27 const int kAnimationOffsetY = 3;
41 28
42 // Duration for the reveal show/hide slide animation. The slower duration is 29 // The height of the region in pixels at the top edge of the screen in which to
43 // used for the initial slide out to give the user more change to see what 30 // steal touch events targetted at the web contents while in immersive
44 // happened. 31 // fullscreen. This region is used to allow us to get edge gestures even if the
45 const int kRevealSlowAnimationDurationMs = 400; 32 // web contents consumes all touch events.
46 const int kRevealFastAnimationDurationMs = 200; 33 const int kStealTouchEventsFromWebContentsRegionHeightPx = 8;
47 34
48 // The delay in milliseconds between the mouse stopping at the top edge of the 35 // Converts from ImmersiveModeController::AnimateReveal to
49 // screen and the top-of-window views revealing. 36 // ash::ImmersiveFullscreenController::AnimateReveal.
50 const int kMouseRevealDelayMs = 200; 37 ash::ImmersiveFullscreenController::AnimateReveal
51 38 ToImmersiveFullscreenControllerAnimateReveal(
52 // The maximum amount of pixels that the cursor can move for the cursor to be 39 ImmersiveModeController::AnimateReveal animate_reveal) {
53 // considered "stopped". This allows the user to reveal the top-of-window views 40 switch (animate_reveal) {
54 // without holding the cursor completely still. 41 case ImmersiveModeController::ANIMATE_REVEAL_YES:
55 const int kMouseRevealXThresholdPixels = 3; 42 return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_YES;
56 43 case ImmersiveModeController::ANIMATE_REVEAL_NO:
57 // How many pixels a gesture can start away from |top_container_| when in 44 return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
58 // closed state and still be considered near it. This is needed to overcome 45 }
59 // issues with poor location values near the edge of the display. 46 NOTREACHED();
60 const int kNearTopContainerDistance = 8; 47 return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
61
62 // Used to multiply x value of an update in check to determine if gesture is
63 // vertical. This is used to make sure that gesture is close to vertical instead
64 // of just more vertical then horizontal.
65 const int kSwipeVerticalThresholdMultiplier = 3;
66
67 // The height in pixels of the region above the top edge of the display which
68 // hosts the immersive fullscreen window in which mouse events are ignored
69 // (cannot reveal or unreveal the top-of-window views).
70 // See ShouldIgnoreMouseEventAtLocation() for more details.
71 const int kHeightOfDeadRegionAboveTopContainer = 10;
72
73 // The height in pixels of the region below the top edge of the display in which
74 // the mouse can trigger revealing the top-of-window views. The height must be
75 // greater than 1px because the top pixel is used to trigger moving the cursor
76 // between displays if the user has a vertical display layout (primary display
77 // above/below secondary display).
78 const int kMouseRevealBoundsHeight = 3;
79
80 // Returns the BubbleDelegateView corresponding to |maybe_bubble| if
81 // |maybe_bubble| is a bubble.
82 views::BubbleDelegateView* AsBubbleDelegate(aura::Window* maybe_bubble) {
83 if (!maybe_bubble)
84 return NULL;
85 views::Widget* widget = views::Widget::GetWidgetForNativeView(maybe_bubble);
86 if (!widget)
87 return NULL;
88 return widget->widget_delegate()->AsBubbleDelegate();
89 } 48 }
90 49
91 // Returns true if |maybe_transient| is a transient child of |toplevel|.
92 bool IsWindowTransientChildOf(aura::Window* maybe_transient,
93 aura::Window* toplevel) {
94 if (!maybe_transient || !toplevel)
95 return false;
96
97 for (aura::Window* window = maybe_transient; window;
98 window = window->transient_parent()) {
99 if (window == toplevel)
100 return true;
101 }
102 return false;
103 }
104
105 // Returns the location of |event| in screen coordinates.
106 gfx::Point GetEventLocationInScreen(const ui::LocatedEvent& event) {
107 gfx::Point location_in_screen = event.location();
108 aura::Window* target = static_cast<aura::Window*>(event.target());
109 aura::client::ScreenPositionClient* screen_position_client =
110 aura::client::GetScreenPositionClient(target->GetRootWindow());
111 screen_position_client->ConvertPointToScreen(target, &location_in_screen);
112 return location_in_screen;
113 }
114
115 ////////////////////////////////////////////////////////////////////////////////
116
117 class RevealedLockAsh : public ImmersiveRevealedLock {
118 public:
119 RevealedLockAsh(const base::WeakPtr<ImmersiveModeControllerAsh>& controller,
120 ImmersiveModeController::AnimateReveal animate_reveal)
121 : controller_(controller) {
122 DCHECK(controller_);
123 controller_->LockRevealedState(animate_reveal);
124 }
125
126 virtual ~RevealedLockAsh() {
127 if (controller_)
128 controller_->UnlockRevealedState();
129 }
130
131 private:
132 base::WeakPtr<ImmersiveModeControllerAsh> controller_;
133
134 DISALLOW_COPY_AND_ASSIGN(RevealedLockAsh);
135 };
136
137 } // namespace 50 } // namespace
138 51
139 ////////////////////////////////////////////////////////////////////////////////
140
141 // Class which keeps the top-of-window views revealed as long as one of the
142 // bubbles it is observing is visible. The logic to keep the top-of-window
143 // views revealed based on the visibility of bubbles anchored to
144 // children of |ImmersiveModeController::top_container_| is separate from
145 // the logic related to |ImmersiveModeControllerAsh::focus_revealed_lock_|
146 // so that bubbles which are not activatable and bubbles which do not close
147 // upon deactivation also keep the top-of-window views revealed for the
148 // duration of their visibility.
149 class ImmersiveModeControllerAsh::BubbleManager : public aura::WindowObserver {
150 public:
151 explicit BubbleManager(ImmersiveModeControllerAsh* controller);
152 virtual ~BubbleManager();
153
154 // Start / stop observing changes to |bubble|'s visibility.
155 void StartObserving(aura::Window* bubble);
156 void StopObserving(aura::Window* bubble);
157
158 private:
159 // Updates |revealed_lock_| based on whether any of |bubbles_| is visible.
160 void UpdateRevealedLock();
161
162 // aura::WindowObserver overrides:
163 virtual void OnWindowVisibilityChanged(aura::Window* window,
164 bool visible) OVERRIDE;
165 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
166
167 ImmersiveModeControllerAsh* controller_;
168
169 std::set<aura::Window*> bubbles_;
170
171 // Lock which keeps the top-of-window views revealed based on whether any of
172 // |bubbles_| is visible.
173 scoped_ptr<ImmersiveRevealedLock> revealed_lock_;
174
175 DISALLOW_COPY_AND_ASSIGN(BubbleManager);
176 };
177
178 ImmersiveModeControllerAsh::BubbleManager::BubbleManager(
179 ImmersiveModeControllerAsh* controller)
180 : controller_(controller) {
181 }
182
183 ImmersiveModeControllerAsh::BubbleManager::~BubbleManager() {
184 for (std::set<aura::Window*>::const_iterator it = bubbles_.begin();
185 it != bubbles_.end(); ++it) {
186 (*it)->RemoveObserver(this);
187 }
188 }
189
190 void ImmersiveModeControllerAsh::BubbleManager::StartObserving(
191 aura::Window* bubble) {
192 if (bubbles_.insert(bubble).second) {
193 bubble->AddObserver(this);
194 UpdateRevealedLock();
195 }
196 }
197
198 void ImmersiveModeControllerAsh::BubbleManager::StopObserving(
199 aura::Window* bubble) {
200 if (bubbles_.erase(bubble)) {
201 bubble->RemoveObserver(this);
202 UpdateRevealedLock();
203 }
204 }
205
206 void ImmersiveModeControllerAsh::BubbleManager::UpdateRevealedLock() {
207 bool has_visible_bubble = false;
208 for (std::set<aura::Window*>::const_iterator it = bubbles_.begin();
209 it != bubbles_.end(); ++it) {
210 if ((*it)->IsVisible()) {
211 has_visible_bubble = true;
212 break;
213 }
214 }
215
216 bool was_revealed = controller_->IsRevealed();
217 if (has_visible_bubble) {
218 if (!revealed_lock_.get()) {
219 // Reveal the top-of-window views without animating because it looks
220 // weird for the top-of-window views to animate and the bubble not to
221 // animate along with the top-of-window views.
222 revealed_lock_.reset(controller_->GetRevealedLock(
223 ImmersiveModeController::ANIMATE_REVEAL_NO));
224 }
225 } else {
226 revealed_lock_.reset();
227 }
228
229 if (!was_revealed && revealed_lock_.get()) {
230 // Currently, there is no nice way for bubbles to reposition themselves
231 // whenever the anchor view moves. Tell the bubbles to reposition themselves
232 // explicitly instead. The hidden bubbles are also repositioned because
233 // BubbleDelegateView does not reposition its widget as a result of a
234 // visibility change.
235 for (std::set<aura::Window*>::const_iterator it = bubbles_.begin();
236 it != bubbles_.end(); ++it) {
237 AsBubbleDelegate(*it)->OnAnchorViewBoundsChanged();
238 }
239 }
240 }
241
242 void ImmersiveModeControllerAsh::BubbleManager::OnWindowVisibilityChanged(
243 aura::Window*,
244 bool visible) {
245 UpdateRevealedLock();
246 }
247
248 void ImmersiveModeControllerAsh::BubbleManager::OnWindowDestroying(
249 aura::Window* window) {
250 StopObserving(window);
251 }
252
253 ////////////////////////////////////////////////////////////////////////////////
254
255 ImmersiveModeControllerAsh::ImmersiveModeControllerAsh() 52 ImmersiveModeControllerAsh::ImmersiveModeControllerAsh()
256 : delegate_(NULL), 53 : controller_(new ash::ImmersiveFullscreenController),
257 widget_(NULL), 54 browser_view_(NULL),
258 top_container_(NULL), 55 native_window_(NULL),
259 observers_enabled_(false), 56 observers_enabled_(false),
260 enabled_(false), 57 use_tab_indicators_(false),
261 reveal_state_(CLOSED), 58 visible_fraction_(1) {
262 revealed_lock_count_(0),
263 tab_indicator_visibility_(TAB_INDICATORS_HIDE),
264 mouse_x_when_hit_top_in_screen_(-1),
265 gesture_begun_(false),
266 native_window_(NULL),
267 animation_(new gfx::SlideAnimation(this)),
268 animations_disabled_for_test_(false),
269 weak_ptr_factory_(this) {
270 } 59 }
271 60
272 ImmersiveModeControllerAsh::~ImmersiveModeControllerAsh() { 61 ImmersiveModeControllerAsh::~ImmersiveModeControllerAsh() {
273 // The browser view is being destroyed so there's no need to update its
274 // layout or layers, even if the top views are revealed. But the window
275 // observers still need to be removed.
276 EnableWindowObservers(false); 62 EnableWindowObservers(false);
277 } 63 }
278 64
279 void ImmersiveModeControllerAsh::LockRevealedState( 65 void ImmersiveModeControllerAsh::Init(BrowserView* browser_view) {
280 AnimateReveal animate_reveal) { 66 browser_view_ = browser_view;
281 ++revealed_lock_count_; 67 native_window_ = browser_view_->GetNativeWindow();
282 Animate animate = (animate_reveal == ANIMATE_REVEAL_YES) ? 68 controller_->Init(this, browser_view_->frame(),
283 ANIMATE_FAST : ANIMATE_NO; 69 browser_view_->top_container());
284 MaybeStartReveal(animate);
285 }
286
287 void ImmersiveModeControllerAsh::UnlockRevealedState() {
288 --revealed_lock_count_;
289 DCHECK_GE(revealed_lock_count_, 0);
290 if (revealed_lock_count_ == 0) {
291 // Always animate ending the reveal fast.
292 MaybeEndReveal(ANIMATE_FAST);
293 }
294 }
295
296 void ImmersiveModeControllerAsh::Init(
297 Delegate* delegate,
298 views::Widget* widget,
299 views::View* top_container) {
300 delegate_ = delegate;
301 widget_ = widget;
302 // Browser view is detached from its widget during destruction. Cache the
303 // window pointer so |this| can stop observing during destruction.
304 native_window_ = widget_->GetNativeWindow();
305 top_container_ = top_container;
306 } 70 }
307 71
308 void ImmersiveModeControllerAsh::SetEnabled(bool enabled) { 72 void ImmersiveModeControllerAsh::SetEnabled(bool enabled) {
309 DCHECK(native_window_) << "Must initialize before enabling"; 73 if (controller_->IsEnabled() == enabled)
310 if (enabled_ == enabled)
311 return; 74 return;
312 enabled_ = enabled;
313 75
314 EnableWindowObservers(enabled_); 76 EnableWindowObservers(enabled);
315 77
316 UpdateUseMinimalChrome(LAYOUT_NO); 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;
317 82
318 if (enabled_) { 83 controller_->SetEnabled(enabled);
319 // Animate enabling immersive mode by sliding out the top-of-window views.
320 // No animation occurs if a lock is holding the top-of-window views open.
321
322 // Do a reveal to set the initial state for the animation. (And any
323 // required state in case the animation cannot run because of a lock holding
324 // the top-of-window views open.) This call has the side effect of relaying
325 // out |browser_view_|'s root view.
326 MaybeStartReveal(ANIMATE_NO);
327
328 // Reset the located event and the focus revealed locks so that they do not
329 // affect whether the top-of-window views are hidden.
330 located_event_revealed_lock_.reset();
331 focus_revealed_lock_.reset();
332
333 // Try doing the animation.
334 MaybeEndReveal(ANIMATE_SLOW);
335
336 if (reveal_state_ == REVEALED) {
337 // Reveal was unsuccessful. Reacquire the revealed locks if appropriate.
338 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
339 UpdateFocusRevealedLock();
340 }
341 } else {
342 // Stop cursor-at-top tracking.
343 top_edge_hover_timer_.Stop();
344 // Snap immediately to the closed state.
345 reveal_state_ = CLOSED;
346 top_container_->SetPaintToLayer(false);
347 delegate_->SetImmersiveStyle(false);
348 SetRenderWindowTopInsetsForTouch(0);
349
350 // Layout the root view so that incognito avatar icon, if any, gets laid
351 // out.
352 LayoutBrowserRootView();
353 }
354 } 84 }
355 85
356 bool ImmersiveModeControllerAsh::IsEnabled() const { 86 bool ImmersiveModeControllerAsh::IsEnabled() const {
357 return enabled_; 87 return controller_->IsEnabled();
358 } 88 }
359 89
360 bool ImmersiveModeControllerAsh::ShouldHideTabIndicators() const { 90 bool ImmersiveModeControllerAsh::ShouldHideTabIndicators() const {
361 return tab_indicator_visibility_ != TAB_INDICATORS_SHOW; 91 return !use_tab_indicators_;
362 } 92 }
363 93
364 bool ImmersiveModeControllerAsh::ShouldHideTopViews() const { 94 bool ImmersiveModeControllerAsh::ShouldHideTopViews() const {
365 return enabled_ && reveal_state_ == CLOSED; 95 return controller_->IsEnabled() && !controller_->IsRevealed();
366 } 96 }
367 97
368 bool ImmersiveModeControllerAsh::IsRevealed() const { 98 bool ImmersiveModeControllerAsh::IsRevealed() const {
369 return enabled_ && reveal_state_ != CLOSED; 99 return controller_->IsRevealed();
370 } 100 }
371 101
372 int ImmersiveModeControllerAsh::GetTopContainerVerticalOffset( 102 int ImmersiveModeControllerAsh::GetTopContainerVerticalOffset(
373 const gfx::Size& top_container_size) const { 103 const gfx::Size& top_container_size) const {
374 if (!enabled_ || reveal_state_ == REVEALED || reveal_state_ == CLOSED) 104 if (!IsEnabled())
375 return 0; 105 return 0;
376 106
377 return animation_->CurrentValueBetween( 107 // The TopContainerView is flush with the top of |browser_view_| when the
378 -top_container_size.height() + kAnimationOffsetY, 0); 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));
379 } 116 }
380 117
381 ImmersiveRevealedLock* ImmersiveModeControllerAsh::GetRevealedLock( 118 ImmersiveRevealedLock* ImmersiveModeControllerAsh::GetRevealedLock(
382 AnimateReveal animate_reveal) { 119 AnimateReveal animate_reveal) {
383 return new RevealedLockAsh(weak_ptr_factory_.GetWeakPtr(), animate_reveal); 120 return controller_->GetRevealedLock(
121 ToImmersiveFullscreenControllerAnimateReveal(animate_reveal));
384 } 122 }
385 123
386 void ImmersiveModeControllerAsh::OnFindBarVisibleBoundsChanged( 124 void ImmersiveModeControllerAsh::OnFindBarVisibleBoundsChanged(
387 const gfx::Rect& new_visible_bounds_in_screen) { 125 const gfx::Rect& new_visible_bounds_in_screen) {
388 find_bar_visible_bounds_in_screen_ = new_visible_bounds_in_screen; 126 find_bar_visible_bounds_in_screen_ = new_visible_bounds_in_screen;
389 } 127 }
390 128
391 void ImmersiveModeControllerAsh::SetupForTest() { 129 void ImmersiveModeControllerAsh::SetupForTest() {
392 DCHECK(!enabled_); 130 controller_->SetupForTest();
393 animations_disabled_for_test_ = true;
394
395 // Move the mouse off of the top-of-window views so that it does not keep
396 // the top-of-window views revealed.
397 gfx::Point cursor_pos(0, top_container_->bounds().bottom() + 100);
398 views::View::ConvertPointToScreen(top_container_, &cursor_pos);
399 aura::Env::GetInstance()->set_last_mouse_location(cursor_pos);
400 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
401 } 131 }
402 132
403 ////////////////////////////////////////////////////////////////////////////////
404 // Observers:
405
406 void ImmersiveModeControllerAsh::Observe(
407 int type,
408 const content::NotificationSource& source,
409 const content::NotificationDetails& details) {
410 DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type);
411 if (enabled_)
412 UpdateUseMinimalChrome(LAYOUT_YES);
413 }
414
415 void ImmersiveModeControllerAsh::OnMouseEvent(ui::MouseEvent* event) {
416 if (!enabled_)
417 return;
418
419 if (event->type() != ui::ET_MOUSE_MOVED &&
420 event->type() != ui::ET_MOUSE_PRESSED &&
421 event->type() != ui::ET_MOUSE_RELEASED &&
422 event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
423 return;
424 }
425
426 // Mouse hover should not initiate revealing the top-of-window views while
427 // |native_window_| is inactive.
428 if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive())
429 return;
430
431 // Mouse hover should not initiate revealing the top-of-window views while
432 // a window has mouse capture.
433 if (aura::client::GetCaptureWindow(native_window_))
434 return;
435
436 if (IsRevealed())
437 UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO);
438
439 // Trigger a reveal if the cursor pauses at the top of the screen for a
440 // while.
441 if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
442 UpdateTopEdgeHoverTimer(event);
443 }
444
445 void ImmersiveModeControllerAsh::OnTouchEvent(ui::TouchEvent* event) {
446 if (!enabled_ || event->type() != ui::ET_TOUCH_PRESSED)
447 return;
448
449 UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO);
450 }
451
452 void ImmersiveModeControllerAsh::OnGestureEvent(ui::GestureEvent* event) {
453 if (!enabled_)
454 return;
455
456 // Touch gestures should not initiate revealing the top-of-window views while
457 // |native_window_| is inactive.
458 if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive())
459 return;
460
461 switch (event->type()) {
462 case ui::ET_GESTURE_SCROLL_BEGIN:
463 if (ShouldHandleGestureEvent(GetEventLocationInScreen(*event))) {
464 gesture_begun_ = true;
465 event->SetHandled();
466 }
467 break;
468 case ui::ET_GESTURE_SCROLL_UPDATE:
469 if (gesture_begun_) {
470 if (UpdateRevealedLocksForSwipe(GetSwipeType(event)))
471 event->SetHandled();
472 gesture_begun_ = false;
473 }
474 break;
475 case ui::ET_GESTURE_SCROLL_END:
476 case ui::ET_SCROLL_FLING_START:
477 gesture_begun_ = false;
478 break;
479 default:
480 break;
481 }
482 }
483
484 void ImmersiveModeControllerAsh::OnWillChangeFocus(views::View* focused_before,
485 views::View* focused_now) {
486 }
487
488 void ImmersiveModeControllerAsh::OnDidChangeFocus(views::View* focused_before,
489 views::View* focused_now) {
490 UpdateFocusRevealedLock();
491 }
492
493 void ImmersiveModeControllerAsh::OnWidgetDestroying(views::Widget* widget) {
494 EnableWindowObservers(false);
495 native_window_ = NULL;
496
497 // Set |enabled_| to false such that any calls to MaybeStartReveal() and
498 // MaybeEndReveal() have no effect.
499 enabled_ = false;
500 }
501
502 void ImmersiveModeControllerAsh::OnWidgetActivationChanged(
503 views::Widget* widget,
504 bool active) {
505 // Mouse hover should not initiate revealing the top-of-window views while
506 // |native_window_| is inactive.
507 top_edge_hover_timer_.Stop();
508
509 UpdateFocusRevealedLock();
510
511 // Allow the top-of-window views to stay revealed if all of the revealed locks
512 // were released in the process of activating |widget| but the mouse is still
513 // hovered above the top-of-window views. For instance, if the bubble which
514 // has been keeping the top-of-window views revealed is hidden but the mouse
515 // is hovered above the top-of-window views, the top-of-window views should
516 // stay revealed. We cannot call UpdateLocatedEventRevealedLock() from
517 // BubbleManager::UpdateRevealedLock() because |widget| is not yet active
518 // at that time.
519 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_YES);
520 }
521
522 ////////////////////////////////////////////////////////////////////////////////
523 // Animation delegate:
524
525 void ImmersiveModeControllerAsh::AnimationEnded(
526 const gfx::Animation* animation) {
527 if (reveal_state_ == SLIDING_OPEN) {
528 // AnimationProgressed() is called immediately before AnimationEnded()
529 // and does a layout.
530 OnSlideOpenAnimationCompleted(LAYOUT_NO);
531 } else if (reveal_state_ == SLIDING_CLOSED) {
532 OnSlideClosedAnimationCompleted();
533 }
534 }
535
536 void ImmersiveModeControllerAsh::AnimationProgressed(
537 const gfx::Animation* animation) {
538 // Relayout. This will also move any views whose position depends on the
539 // top container position such as the find bar.
540 // We do not call LayoutBrowserRootView() here because we are not toggling
541 // the tab strip's immersive style so relaying out the non client view is not
542 // necessary.
543 top_container_->parent()->Layout();
544 }
545
546 ////////////////////////////////////////////////////////////////////////////////
547 // aura::WindowObserver overrides:
548
549 void ImmersiveModeControllerAsh::OnWindowPropertyChanged(aura::Window* window,
550 const void* key,
551 intptr_t old) {
552 if (!enabled_)
553 return;
554
555 if (key == aura::client::kShowStateKey) {
556 // Disable immersive mode when the user exits fullscreen without going
557 // through FullscreenController::ToggleFullscreenMode(). This is the case
558 // if the user exits fullscreen via the restore button.
559 ui::WindowShowState show_state = static_cast<ui::WindowShowState>(
560 native_window_->GetProperty(aura::client::kShowStateKey));
561 if (show_state != ui::SHOW_STATE_FULLSCREEN &&
562 show_state != ui::SHOW_STATE_MINIMIZED) {
563 delegate_->FullscreenStateChanged();
564 }
565 }
566 }
567
568 void ImmersiveModeControllerAsh::OnAddTransientChild(aura::Window* window,
569 aura::Window* transient) {
570 views::BubbleDelegateView* bubble_delegate = AsBubbleDelegate(transient);
571 if (bubble_delegate &&
572 bubble_delegate->GetAnchorView() &&
573 top_container_->Contains(bubble_delegate->GetAnchorView())) {
574 // Observe the aura::Window because the BubbleDelegateView may not be
575 // parented to the widget's root view yet so |bubble_delegate->GetWidget()|
576 // may still return NULL.
577 bubble_manager_->StartObserving(transient);
578 }
579 }
580
581 void ImmersiveModeControllerAsh::OnRemoveTransientChild(
582 aura::Window* window,
583 aura::Window* transient) {
584 bubble_manager_->StopObserving(transient);
585 }
586
587 ////////////////////////////////////////////////////////////////////////////////
588 // private:
589
590 void ImmersiveModeControllerAsh::EnableWindowObservers(bool enable) { 133 void ImmersiveModeControllerAsh::EnableWindowObservers(bool enable) {
591 if (observers_enabled_ == enable) 134 if (observers_enabled_ == enable)
592 return; 135 return;
593 observers_enabled_ = enable; 136 observers_enabled_ = enable;
594 137
595 if (!native_window_) { 138 content::Source<FullscreenController> source(
596 NOTREACHED() << "ImmersiveModeControllerAsh not initialized"; 139 browser_view_->browser()->fullscreen_controller());
597 return;
598 }
599
600 views::Widget* widget =
601 views::Widget::GetWidgetForNativeWindow(native_window_);
602 views::FocusManager* focus_manager = widget->GetFocusManager();
603 if (enable) { 140 if (enable) {
604 widget->AddObserver(this); 141 ash::wm::GetWindowState(native_window_)->AddObserver(this);
605 focus_manager->AddFocusChangeListener(this); 142 registrar_.Add(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, source);
606 } else { 143 } else {
607 widget->RemoveObserver(this); 144 ash::wm::GetWindowState(native_window_)->RemoveObserver(this);
608 focus_manager->RemoveFocusChangeListener(this); 145 registrar_.Remove(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, source);
609 }
610
611 if (enable)
612 ash::Shell::GetInstance()->AddPreTargetHandler(this);
613 else
614 ash::Shell::GetInstance()->RemovePreTargetHandler(this);
615
616 if (enable) {
617 native_window_->AddObserver(this);
618 } else {
619 native_window_->RemoveObserver(this);
620 }
621
622 if (enable) {
623 RecreateBubbleManager();
624 } else {
625 // We have stopped observing whether transient children are added or removed
626 // to |native_window_|. The set of bubbles that BubbleManager is observing
627 // will become stale really quickly. Destroy BubbleManager and recreate it
628 // when we start observing |native_window_| again.
629 bubble_manager_.reset();
630 }
631
632 if (enable) {
633 registrar_.Add(
634 this,
635 chrome::NOTIFICATION_FULLSCREEN_CHANGED,
636 content::Source<FullscreenController>(
637 delegate_->GetFullscreenController()));
638 } else {
639 registrar_.Remove(
640 this,
641 chrome::NOTIFICATION_FULLSCREEN_CHANGED,
642 content::Source<FullscreenController>(
643 delegate_->GetFullscreenController()));
644 }
645
646 if (!enable)
647 animation_->Stop();
648 }
649
650 void ImmersiveModeControllerAsh::UpdateTopEdgeHoverTimer(
651 ui::MouseEvent* event) {
652 DCHECK(enabled_);
653 // Stop the timer if the top-of-window views are already revealed.
654 if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
655 top_edge_hover_timer_.Stop();
656 return;
657 }
658
659 gfx::Point location_in_screen = GetEventLocationInScreen(*event);
660 if (ShouldIgnoreMouseEventAtLocation(location_in_screen))
661 return;
662
663 // Stop the timer if the cursor left the top edge or is on a different
664 // display. The bounds of |top_container_|'s parent are used to infer the hit
665 // bounds because |top_container_| will be partially offscreen if it is
666 // animating closed.
667 gfx::Rect hit_bounds_in_screen =
668 top_container_->parent()->GetBoundsInScreen();
669 hit_bounds_in_screen.set_height(kMouseRevealBoundsHeight);
670 if (!hit_bounds_in_screen.Contains(location_in_screen)) {
671 top_edge_hover_timer_.Stop();
672 return;
673 }
674
675 // The cursor is now at the top of the screen. Consider the cursor "not
676 // moving" even if it moves a little bit because users don't have perfect
677 // pointing precision. (The y position is not tested because
678 // |hit_bounds_in_screen| is short.)
679 if (top_edge_hover_timer_.IsRunning() &&
680 abs(location_in_screen.x() - mouse_x_when_hit_top_in_screen_) <=
681 kMouseRevealXThresholdPixels)
682 return;
683
684 // Start the reveal if the cursor doesn't move for some amount of time.
685 mouse_x_when_hit_top_in_screen_ = location_in_screen.x();
686 top_edge_hover_timer_.Stop();
687 // Timer is stopped when |this| is destroyed, hence Unretained() is safe.
688 top_edge_hover_timer_.Start(
689 FROM_HERE,
690 base::TimeDelta::FromMilliseconds(kMouseRevealDelayMs),
691 base::Bind(&ImmersiveModeControllerAsh::AcquireLocatedEventRevealedLock,
692 base::Unretained(this)));
693 }
694
695 void ImmersiveModeControllerAsh::UpdateLocatedEventRevealedLock(
696 ui::LocatedEvent* event,
697 AllowRevealWhileClosing allow_reveal_while_closing) {
698 if (!enabled_)
699 return;
700 DCHECK(!event || event->IsMouseEvent() || event->IsTouchEvent());
701
702 // Neither the mouse nor touch can initiate a reveal when the top-of-window
703 // views are sliding closed or are closed with the following exceptions:
704 // - Hovering at y = 0 which is handled in OnMouseEvent().
705 // - Doing a SWIPE_OPEN edge gesture which is handled in OnGestureEvent().
706 if (reveal_state_ == CLOSED ||
707 (reveal_state_ == SLIDING_CLOSED &&
708 allow_reveal_while_closing == ALLOW_REVEAL_WHILE_CLOSING_NO)) {
709 return;
710 }
711
712 // Neither the mouse nor touch should keep the top-of-window views revealed if
713 // |native_window_| is not active.
714 if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()) {
715 located_event_revealed_lock_.reset();
716 return;
717 }
718
719 // Ignore all events while a window has capture. This keeps the top-of-window
720 // views revealed during a drag.
721 if (aura::client::GetCaptureWindow(native_window_))
722 return;
723
724 gfx::Point location_in_screen;
725 if (event && event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
726 location_in_screen = GetEventLocationInScreen(*event);
727 } else {
728 aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(
729 native_window_->GetRootWindow());
730 if (!cursor_client->IsMouseEventsEnabled()) {
731 // If mouse events are disabled, the user's last interaction was probably
732 // via touch. Do no do further processing in this case as there is no easy
733 // way of retrieving the position of the user's last touch.
734 return;
735 }
736 location_in_screen = aura::Env::GetInstance()->last_mouse_location();
737 }
738
739 if ((!event || event->IsMouseEvent()) &&
740 ShouldIgnoreMouseEventAtLocation(location_in_screen)) {
741 return;
742 }
743
744 gfx::Rect hit_bounds_in_top_container = top_container_->GetVisibleBounds();
745 // TODO(tdanderson): Implement View::ConvertRectToScreen();
746 gfx::Point hit_bounds_in_screen_origin = hit_bounds_in_top_container.origin();
747 views::View::ConvertPointToScreen(top_container_,
748 &hit_bounds_in_screen_origin);
749 gfx::Rect hit_bounds_in_screen(hit_bounds_in_screen_origin,
750 hit_bounds_in_top_container.size());
751
752 gfx::Rect find_bar_hit_bounds_in_screen = find_bar_visible_bounds_in_screen_;
753
754 // Allow the cursor to move slightly off the top-of-window views before
755 // sliding closed. This helps when the user is attempting to click on the
756 // bookmark bar and overshoots slightly.
757 if (event && event->type() == ui::ET_MOUSE_MOVED) {
758 const int kBoundsOffsetY = 8;
759 hit_bounds_in_screen.Inset(0, 0, 0, -kBoundsOffsetY);
760 find_bar_hit_bounds_in_screen.Inset(0, 0, 0, -kBoundsOffsetY);
761 }
762
763 if (hit_bounds_in_screen.Contains(location_in_screen) ||
764 find_bar_hit_bounds_in_screen.Contains(location_in_screen)) {
765 AcquireLocatedEventRevealedLock();
766 } else {
767 located_event_revealed_lock_.reset();
768 } 146 }
769 } 147 }
770 148
771 void ImmersiveModeControllerAsh::AcquireLocatedEventRevealedLock() {
772 // CAUTION: Acquiring the lock results in a reentrant call to
773 // AcquireLocatedEventRevealedLock() when
774 // |ImmersiveModeControllerAsh::animations_disabled_for_test_| is true.
775 if (!located_event_revealed_lock_.get())
776 located_event_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES));
777 }
778
779 void ImmersiveModeControllerAsh::UpdateFocusRevealedLock() {
780 if (!enabled_)
781 return;
782
783 bool hold_lock = false;
784 views::Widget* widget =
785 views::Widget::GetWidgetForNativeWindow(native_window_);
786 if (widget->IsActive()) {
787 views::View* focused_view = widget->GetFocusManager()->GetFocusedView();
788 if (top_container_->Contains(focused_view))
789 hold_lock = true;
790 } else {
791 aura::Window* active_window = aura::client::GetActivationClient(
792 native_window_->GetRootWindow())->GetActiveWindow();
793 views::BubbleDelegateView* bubble_delegate =
794 AsBubbleDelegate(active_window);
795 if (bubble_delegate && bubble_delegate->anchor_widget()) {
796 // BubbleManager will already have locked the top-of-window views if the
797 // bubble is anchored to a child of |top_container_|. Don't acquire
798 // |focus_revealed_lock_| here for the sake of simplicity.
799 // Note: Instead of checking for the existence of the |anchor_view|,
800 // the existence of the |anchor_widget| is performed to avoid the case
801 // where the view is already gone (and the widget is still running).
802 } else {
803 // The currently active window is not |native_window_| and it is not a
804 // bubble with an anchor view. The top-of-window views should be revealed
805 // if:
806 // 1) The active window is a transient child of |native_window_|.
807 // 2) The top-of-window views are already revealed. This restriction
808 // prevents a transient window opened by the web contents while the
809 // top-of-window views are hidden from from initiating a reveal.
810 // The top-of-window views will stay revealed till |native_window_| is
811 // reactivated.
812 if (IsRevealed() &&
813 IsWindowTransientChildOf(active_window, native_window_)) {
814 hold_lock = true;
815 }
816 }
817 }
818
819 if (hold_lock) {
820 if (!focus_revealed_lock_.get())
821 focus_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES));
822 } else {
823 focus_revealed_lock_.reset();
824 }
825 }
826
827 bool ImmersiveModeControllerAsh::UpdateRevealedLocksForSwipe(
828 SwipeType swipe_type) {
829 if (!enabled_ || swipe_type == SWIPE_NONE)
830 return false;
831
832 // Swipes while |native_window_| is inactive should have been filtered out in
833 // OnGestureEvent().
834 DCHECK(views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive());
835
836 if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) {
837 if (swipe_type == SWIPE_OPEN && !located_event_revealed_lock_.get()) {
838 located_event_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES));
839 return true;
840 }
841 } else {
842 if (swipe_type == SWIPE_CLOSE) {
843 // Attempt to end the reveal. If other code is holding onto a lock, the
844 // attempt will be unsuccessful.
845 located_event_revealed_lock_.reset();
846 focus_revealed_lock_.reset();
847
848 if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED)
849 return true;
850
851 // Ending the reveal was unsuccessful. Reaquire the locks if appropriate.
852 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
853 UpdateFocusRevealedLock();
854 }
855 }
856 return false;
857 }
858
859 void ImmersiveModeControllerAsh::UpdateUseMinimalChrome(Layout layout) {
860 // May be NULL in tests.
861 FullscreenController* fullscreen_controller =
862 delegate_->GetFullscreenController();
863 bool in_tab_fullscreen = fullscreen_controller ?
864 fullscreen_controller->IsFullscreenForTabOrPending() : false;
865 bool use_minimal_chrome = !in_tab_fullscreen && enabled_;
866
867 // When using minimal chrome, the shelf is auto-hidden. The auto-hidden shelf
868 // displays a 3px 'light bar' when it is closed. Otherwise, the shelf is
869 // hidden completely and cannot be revealed.
870 ash::wm::GetWindowState(native_window_)->set_hide_shelf_when_fullscreen(
871 !use_minimal_chrome);
872
873 TabIndicatorVisibility previous_tab_indicator_visibility =
874 tab_indicator_visibility_;
875 if (tab_indicator_visibility_ != TAB_INDICATORS_FORCE_HIDE) {
876 tab_indicator_visibility_ = use_minimal_chrome ?
877 TAB_INDICATORS_SHOW : TAB_INDICATORS_HIDE;
878 }
879
880 ash::Shell::GetInstance()->UpdateShelfVisibility();
881
882 if (tab_indicator_visibility_ != previous_tab_indicator_visibility) {
883 // If the top-of-window views are revealed or animating, the change will
884 // take effect with the layout once the top-of-window views are closed.
885 if (layout == LAYOUT_YES && reveal_state_ == CLOSED)
886 LayoutBrowserRootView();
887 }
888 }
889
890 int ImmersiveModeControllerAsh::GetAnimationDuration(Animate animate) const {
891 switch (animate) {
892 case ANIMATE_NO:
893 return 0;
894 case ANIMATE_SLOW:
895 return kRevealSlowAnimationDurationMs;
896 case ANIMATE_FAST:
897 return kRevealFastAnimationDurationMs;
898 }
899 NOTREACHED();
900 return 0;
901 }
902
903 void ImmersiveModeControllerAsh::MaybeStartReveal(Animate animate) {
904 if (!enabled_)
905 return;
906
907 if (animations_disabled_for_test_)
908 animate = ANIMATE_NO;
909
910 // Callers with ANIMATE_NO expect this function to synchronously reveal the
911 // top-of-window views.
912 if (reveal_state_ == REVEALED ||
913 (reveal_state_ == SLIDING_OPEN && animate != ANIMATE_NO)) {
914 return;
915 }
916
917 RevealState previous_reveal_state = reveal_state_;
918 reveal_state_ = SLIDING_OPEN;
919 if (previous_reveal_state == CLOSED) {
920 // Turn on layer painting so that we can overlap the web contents.
921 top_container_->SetPaintToLayer(true);
922
923 // Ensure window caption buttons are updated and the view bounds are
924 // computed at normal (non-immersive-style) size. The layout call moves the
925 // top-of-window views to their initial offscreen position for the
926 // animation.
927 delegate_->SetImmersiveStyle(false);
928 SetRenderWindowTopInsetsForTouch(0);
929 LayoutBrowserRootView();
930
931 // Do not do any more processing if LayoutBrowserView() changed
932 // |reveal_state_|.
933 if (reveal_state_ != SLIDING_OPEN) {
934 if (reveal_state_ == REVEALED)
935 FOR_EACH_OBSERVER(Observer, observers_, OnImmersiveRevealStarted());
936 return;
937 }
938 }
939 // Slide in the reveal view.
940 if (animate == ANIMATE_NO) {
941 animation_->Reset(1);
942 OnSlideOpenAnimationCompleted(LAYOUT_YES);
943 } else {
944 animation_->SetSlideDuration(GetAnimationDuration(animate));
945 animation_->Show();
946 }
947
948 if (previous_reveal_state == CLOSED)
949 FOR_EACH_OBSERVER(Observer, observers_, OnImmersiveRevealStarted());
950 }
951
952 void ImmersiveModeControllerAsh::LayoutBrowserRootView() { 149 void ImmersiveModeControllerAsh::LayoutBrowserRootView() {
150 views::Widget* widget = browser_view_->frame();
953 // Update the window caption buttons. 151 // Update the window caption buttons.
954 widget_->non_client_view()->frame_view()->ResetWindowControls(); 152 widget->non_client_view()->frame_view()->ResetWindowControls();
955 // Layout all views, including BrowserView. 153 // Layout all views, including BrowserView.
956 widget_->GetRootView()->Layout(); 154 widget->GetRootView()->Layout();
957 }
958
959 void ImmersiveModeControllerAsh::OnSlideOpenAnimationCompleted(Layout layout) {
960 DCHECK_EQ(SLIDING_OPEN, reveal_state_);
961 reveal_state_ = REVEALED;
962
963 if (layout == LAYOUT_YES)
964 top_container_->parent()->Layout();
965
966 // The user may not have moved the mouse since the reveal was initiated.
967 // Update the revealed lock to reflect the mouse's current state.
968 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
969 }
970
971 void ImmersiveModeControllerAsh::MaybeEndReveal(Animate animate) {
972 if (!enabled_ || revealed_lock_count_ != 0)
973 return;
974
975 if (animations_disabled_for_test_)
976 animate = ANIMATE_NO;
977
978 // Callers with ANIMATE_NO expect this function to synchronously close the
979 // top-of-window views.
980 if (reveal_state_ == CLOSED ||
981 (reveal_state_ == SLIDING_CLOSED && animate != ANIMATE_NO)) {
982 return;
983 }
984
985 reveal_state_ = SLIDING_CLOSED;
986 int duration_ms = GetAnimationDuration(animate);
987 if (duration_ms > 0) {
988 animation_->SetSlideDuration(duration_ms);
989 animation_->Hide();
990 } else {
991 animation_->Reset(0);
992 OnSlideClosedAnimationCompleted();
993 }
994 }
995
996 void ImmersiveModeControllerAsh::OnSlideClosedAnimationCompleted() {
997 DCHECK_EQ(SLIDING_CLOSED, reveal_state_);
998 reveal_state_ = CLOSED;
999 // Layers aren't needed after animation completes.
1000 top_container_->SetPaintToLayer(false);
1001 // Update tabstrip for closed state.
1002 delegate_->SetImmersiveStyle(true);
1003 SetRenderWindowTopInsetsForTouch(kNearTopContainerDistance);
1004 LayoutBrowserRootView();
1005 }
1006
1007 ImmersiveModeControllerAsh::SwipeType ImmersiveModeControllerAsh::GetSwipeType(
1008 ui::GestureEvent* event) const {
1009 if (event->type() != ui::ET_GESTURE_SCROLL_UPDATE)
1010 return SWIPE_NONE;
1011 // Make sure that it is a clear vertical gesture.
1012 if (abs(event->details().scroll_y()) <=
1013 kSwipeVerticalThresholdMultiplier * abs(event->details().scroll_x()))
1014 return SWIPE_NONE;
1015 if (event->details().scroll_y() < 0)
1016 return SWIPE_CLOSE;
1017 else if (event->details().scroll_y() > 0)
1018 return SWIPE_OPEN;
1019 return SWIPE_NONE;
1020 }
1021
1022 bool ImmersiveModeControllerAsh::ShouldIgnoreMouseEventAtLocation(
1023 const gfx::Point& location) const {
1024 // Ignore mouse events in the region immediately above the top edge of the
1025 // display. This is to handle the case of a user with a vertical display
1026 // layout (primary display above/below secondary display) and the immersive
1027 // fullscreen window on the bottom display. It is really hard to trigger a
1028 // reveal in this case because:
1029 // - It is hard to stop the cursor in the top |kMouseRevealBoundsHeight|
1030 // pixels of the bottom display.
1031 // - The cursor is warped to the top display if the cursor gets to the top
1032 // edge of the bottom display.
1033 // Mouse events are ignored in the bottom few pixels of the top display
1034 // (Mouse events in this region cannot start or end a reveal). This allows a
1035 // user to overshoot the top of the bottom display and still reveal the
1036 // top-of-window views.
1037 gfx::Rect dead_region = top_container_->parent()->GetBoundsInScreen();
1038 dead_region.set_y(dead_region.y() - kHeightOfDeadRegionAboveTopContainer);
1039 dead_region.set_height(kHeightOfDeadRegionAboveTopContainer);
1040 return dead_region.Contains(location);
1041 }
1042
1043 bool ImmersiveModeControllerAsh::ShouldHandleGestureEvent(
1044 const gfx::Point& location) const {
1045 gfx::Rect top_container_bounds_in_screen =
1046 top_container_->GetBoundsInScreen();
1047
1048 // All of the gestures that are of interest start in a region with left &
1049 // right edges agreeing with |top_container_|. When CLOSED it is difficult to
1050 // hit the bounds due to small size of the tab strip, so the hit target needs
1051 // to be extended on the bottom, thus the inset call.
1052 gfx::Rect near_bounds = top_container_bounds_in_screen;
1053 if (reveal_state_ == CLOSED)
1054 near_bounds.Inset(gfx::Insets(0, 0, -kNearTopContainerDistance, 0));
1055 if (near_bounds.Contains(location))
1056 return true;
1057
1058 // There may be a bezel sensor off screen logically above |top_container_|
1059 // thus the test needs to include gestures starting above, but this needs to
1060 // be distinguished from events originating on another screen from
1061 // (potentially) an extended desktop. The check for the event not contained by
1062 // the closest screen ensures that the event is from a valid bezel and can be
1063 // interpreted as such.
1064 gfx::Rect screen_bounds =
1065 ash::Shell::GetScreen()->GetDisplayNearestPoint(location).bounds();
1066 return (!screen_bounds.Contains(location) &&
1067 location.y() < top_container_bounds_in_screen.y() &&
1068 location.x() >= top_container_bounds_in_screen.x() &&
1069 location.x() < top_container_bounds_in_screen.right());
1070 } 155 }
1071 156
1072 void ImmersiveModeControllerAsh::SetRenderWindowTopInsetsForTouch( 157 void ImmersiveModeControllerAsh::SetRenderWindowTopInsetsForTouch(
1073 int top_inset) { 158 int top_inset) {
1074 content::WebContents* contents = delegate_->GetWebContents(); 159 content::WebContents* contents = browser_view_->GetActiveWebContents();
1075 if (contents) { 160 if (contents) {
1076 aura::Window* window = contents->GetView()->GetContentNativeView(); 161 aura::Window* window = contents->GetView()->GetContentNativeView();
1077 // |window| is NULL if the renderer crashed. 162 // |window| is NULL if the renderer crashed.
1078 if (window) { 163 if (window) {
1079 gfx::Insets inset(top_inset, 0, 0, 0); 164 gfx::Insets inset(top_inset, 0, 0, 0);
1080 window->SetHitTestBoundsOverrideOuter( 165 window->SetHitTestBoundsOverrideOuter(
1081 window->hit_test_bounds_override_outer_mouse(), 166 window->hit_test_bounds_override_outer_mouse(),
1082 inset); 167 inset);
1083 } 168 }
1084 } 169 }
1085 } 170 }
1086 171
1087 void ImmersiveModeControllerAsh::RecreateBubbleManager() { 172 void ImmersiveModeControllerAsh::SetTabIndicatorsVisible(bool visible) {
1088 bubble_manager_.reset(new BubbleManager(this)); 173 DCHECK(!visible || use_tab_indicators_);
1089 const std::vector<aura::Window*> transient_children = 174 if (browser_view_->tabstrip())
1090 native_window_->transient_children(); 175 browser_view_->tabstrip()->SetImmersiveStyle(visible);
1091 for (size_t i = 0; i < transient_children.size(); ++i) { 176 }
1092 aura::Window* transient_child = transient_children[i]; 177
1093 views::BubbleDelegateView* bubble_delegate = 178 void ImmersiveModeControllerAsh::OnImmersiveRevealStarted() {
1094 AsBubbleDelegate(transient_child); 179 visible_fraction_ = 0;
1095 if (bubble_delegate && 180 browser_view_->top_container()->SetPaintToLayer(true);
1096 bubble_delegate->GetAnchorView() && 181 SetTabIndicatorsVisible(false);
1097 top_container_->Contains(bubble_delegate->GetAnchorView())) { 182 SetRenderWindowTopInsetsForTouch(0);
1098 bubble_manager_->StartObserving(transient_child); 183 LayoutBrowserRootView();
1099 } 184 FOR_EACH_OBSERVER(Observer, observers_, OnImmersiveRevealStarted());
185 }
186
187 void 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
196 void ImmersiveModeControllerAsh::OnImmersiveFullscreenExited() {
197 browser_view_->top_container()->SetPaintToLayer(false);
198 SetTabIndicatorsVisible(false);
199 SetRenderWindowTopInsetsForTouch(0);
200 LayoutBrowserRootView();
201 }
202
203 void ImmersiveModeControllerAsh::SetVisibleFraction(double visible_fraction) {
204 if (visible_fraction_ != visible_fraction) {
205 visible_fraction_ = visible_fraction;
206 browser_view_->Layout();
1100 } 207 }
1101 } 208 }
209
210 std::vector<gfx::Rect>
211 ImmersiveModeControllerAsh::GetVisibleBoundsInScreen() {
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
229 void 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
242 void 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698