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