blob: f2a23afd410e2b2ba89f7d85a55071cc54642339 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/optional.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/browser/ui/ash/tablet_mode_client_observer.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/views/frame/top_controls_slide_controller.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
class BrowserView;
class TopControlsSlideTabObserver;
// Implements the Android-like slide behavior of the browser top controls for
// Chrome OS. This behavior is enabled only in tablet mode when the browser is
// not in immersive fullscreen mode. The browser top controls (a.k.a.
// top-chrome) shows and hides with page gesture scrolls.
// This controller tracks the current values of the top controls shown ratio for
// the entire browser, as well as for each tab. The values tracked per tabs
// mirror the values kept in each renderer of the corresponding tab.
// There are many conditions that should fully show the browser top controls if
// they're fully hidden. Examples are:
// - Switching, creating, or removing tabs.
// - Tab's renderer process crashing or hanging.
// - Focusing on an editable element within a web-page.
// - Exiting tablet mode.
// - Entering immersive fullscreen mode.
// - Page security level changes.
class TopControlsSlideControllerChromeOS
: public TopControlsSlideController,
public TabletModeClientObserver,
public TabStripModelObserver,
public content::NotificationObserver {
explicit TopControlsSlideControllerChromeOS(BrowserView* browser_view);
~TopControlsSlideControllerChromeOS() override;
// TopControlsSlideController:
bool IsEnabled() const override;
float GetShownRatio() const override;
void SetShownRatio(content::WebContents* contents, float ratio) override;
void OnBrowserFullscreenStateWillChange(bool new_fullscreen_state) override;
bool DoBrowserControlsShrinkRendererSize(
const content::WebContents* contents) const override;
void SetTopControlsGestureScrollInProgress(bool in_progress) override;
bool IsTopControlsGestureScrollInProgress() const override;
// TabletModeClientObserver:
void OnTabletModeToggled(bool tablet_mode_enabled) override;
// TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override;
void SetTabNeedsAttentionAt(int index, bool attention) override;
// content::NotificationObserver:
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
// Returns true if this feature can be turned on. If |fullscreen_state| is
// supplied, it will be used in calculating the result, otherwise the current
// fullscreen state will be queried from BrowserView. This is needed since
// BrowserView informs us with fullscreen state changes before they happen
// (See OnBrowserFullscreenStateWillChange()) so that we can disable the
// sliding behavior *before* immersive mode is entered.
bool CanEnable(base::Optional<bool> fullscreen_state) const;
// Called back from the AccessibilityManager so that we're updated by the
// status of Chromevox, which when enabled, sliding the top-controls should
// be disabled. This is important for users who want to touch explore and need
// this to be consistent.
void OnAccessibilityStatusChanged(
const chromeos::AccessibilityStatusEventDetails& event_details);
void OnEnabledStateChanged(bool new_state);
// Refreshes the status of the browser top controls.
void Refresh();
// Prepares for sliding the browser top controls by creating the necessary
// layers, adjusting bounds and laying out the BrowserView one last time
// before we enter the transient state, during which layer transforms are used
// to slide the top controls.
void OnBeginSliding();
// Prepares for entering the steady state where the top controls reach their
// final positions, and the |shown_ratio_| reaches either one of its terminal
// values (1.f or 0.f). Layer transforms are reset to identity, and the
// BrowserView is laid out into its final bounds.
void OnEndSliding();
// Updates whether the currently active tab has shrunk its renderer's viewport
// size.
void UpdateDoBrowserControlsShrinkRendererSize();
BrowserView* browser_view_;
// Represents the per-browser (as opposed to per-tab) shown ratio of the top
// controls that is currently applied.
float shown_ratio_ = 1.f;
// Indicates whether sliding the top controls with gesture scrolls is
// currently enabled, which is true when tablet mode is enabled and the
// browser window is not full-screened. This value is cached here since it
// needs to be queried whenever we get an update from the renderer to adjust
// the shown ratio. These updates result from touch gesture scrolls, so we
// need to minimize the work we do to get these values, so sliding the browser
// top controls feels smooth.
bool is_enabled_ = false;
// Whether we need to wait for the renderer to set the shown ratio to 1.f
// before we toggle |is_enabled_| to false. It is used to postpone disabling
// top-chrome sliding until the renderer responds so that we can make sure
// both the renderer and the browser are both synchronized.
bool defer_disabling_ = false;
// Indicates whether a touch gesture scrolling is in progress. This value is
// updated by the renderer when it receives a GestureEventAck of type either
// kGestureScrollBegin or kGestureScrollEnd.
bool is_gesture_scrolling_in_progress_ = false;
// Indicates that the browser top controls are sliding up or down. This is
// different from |is_gesture_scrolling_in_progress_| above. The top controls
// may be sliding due an in-progress gesture scrolls or due to a renderer-
// managed animation (such as in response to showing tabs or focusing on an
// editable element within the page).
// As long as this value is true, we are in a transient state, and layer
// transforms are used to slide the top controls for efficiency. Once it turns
// false, the layer transforms are reset to identity and the browser view is
// re-laid out.
bool is_sliding_in_progress_ = false;
// We need to observe the tab's web contents to listen to events that affect
// the browser top controls shown state for each tab.
content::NotificationRegistrar registrar_;