blob: a780ffcc61d88e670ae51b01f5cd63533670cbee [file] [log] [blame]
// Copyright 2016 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 "third_party/blink/renderer/core/frame/browser_controls.h"
#include <algorithm> // for std::min and std::max
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/platform/geometry/float_size.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
namespace blink {
BrowserControls::BrowserControls(const Page& page)
: page_(&page),
top_shown_ratio_(0),
bottom_shown_ratio_(0),
baseline_top_content_offset_(0),
baseline_bottom_content_offset_(0),
accumulated_scroll_delta_(0),
permitted_state_(cc::BrowserControlsState::kBoth) {}
void BrowserControls::Trace(Visitor* visitor) {
visitor->Trace(page_);
}
void BrowserControls::ScrollBegin() {
ResetBaseline();
}
FloatSize BrowserControls::ScrollBy(FloatSize pending_delta) {
// If one or both of the top/bottom controls are showing, the shown ratio
// needs to be computed.
if (!TopHeight() && !BottomHeight())
return pending_delta;
if ((permitted_state_ == cc::BrowserControlsState::kShown &&
pending_delta.Height() > 0) ||
(permitted_state_ == cc::BrowserControlsState::kHidden &&
pending_delta.Height() < 0))
return pending_delta;
float page_scale = page_->GetVisualViewport().Scale();
// Update accumulated vertical scroll and apply it to browser controls
// Compute scroll delta in viewport space by applying page scale
accumulated_scroll_delta_ += pending_delta.Height() * page_scale;
// We want to base our calculations on top or bottom controls. After consuming
// the scroll delta, we will calculate a shown ratio for the controls. The
// top controls have the priority because they need to visually be in sync
// with the web contents.
bool base_on_top_controls = TopHeight();
float old_top_offset = ContentOffset();
float baseline_content_offset = base_on_top_controls
? baseline_top_content_offset_
: baseline_bottom_content_offset_;
float new_content_offset =
baseline_content_offset - accumulated_scroll_delta_;
float height = base_on_top_controls ? TopHeight() : BottomHeight();
// Clamp and use the expected content offset so that we don't return
// spurious remaining scrolls due to the imprecision of the shownRatio.
new_content_offset = clampTo(
new_content_offset,
base_on_top_controls ? TopMinHeight() : BottomMinHeight(), height);
// The top and bottom controls ratios can be calculated independently.
// However, we want the (normalized) ratios to be equal when scrolling.
float shown_ratio = new_content_offset / height;
float min_ratio =
base_on_top_controls ? TopMinShownRatio() : BottomMinShownRatio();
float normalized_shown_ratio =
(clampTo(shown_ratio, min_ratio, 1.f) - min_ratio) / (1.f - min_ratio);
// Even though the real shown ratios (shown height / total height) of the top
// and bottom controls can be different, they share the same
// relative/normalized ratio to keep them in sync.
SetShownRatio(
TopMinShownRatio() + normalized_shown_ratio * (1.f - TopMinShownRatio()),
BottomMinShownRatio() +
normalized_shown_ratio * (1.f - BottomMinShownRatio()));
// Reset baseline when controls are fully visible
if (top_shown_ratio_ == 1 && bottom_shown_ratio_ == 1)
ResetBaseline();
// We negate the difference because scrolling down (positive delta) causes
// browser controls to hide (negative offset difference).
FloatSize applied_delta(
0, base_on_top_controls
? (old_top_offset - new_content_offset) / page_scale
: 0);
return pending_delta - applied_delta;
}
void BrowserControls::ScrollEnd() {
if ((top_shown_ratio_ == TopMinShownRatio() || top_shown_ratio_ == 1) &&
(bottom_shown_ratio_ == BottomMinShownRatio() ||
bottom_shown_ratio_ == 1)) {
return;
}
// Both threshold values are copied from LayerTreeSettings, which are used in
// BrowserControlsOffsetManager::ScrollEnd.
constexpr float kTopControlsShowThreshold = 0.5f;
constexpr float kTopControlsHideThreshold = 0.5f;
float normalized_top_ratio =
(top_shown_ratio_ - TopMinShownRatio()) / (1.f - TopMinShownRatio());
if (normalized_top_ratio >= 1.f - kTopControlsHideThreshold) {
// If we're showing so much that the hide threshold won't trigger, show.
UpdateConstraintsAndState(permitted_state_,
cc::BrowserControlsState::kShown);
} else if (normalized_top_ratio < kTopControlsShowThreshold) {
// If we're showing so little that the show threshold won't trigger, hide.
UpdateConstraintsAndState(permitted_state_,
cc::BrowserControlsState::kHidden);
} else {
NOTREACHED();
}
}
void BrowserControls::ResetBaseline() {
accumulated_scroll_delta_ = 0;
baseline_top_content_offset_ = ContentOffset();
baseline_bottom_content_offset_ = BottomContentOffset();
}
float BrowserControls::UnreportedSizeAdjustment() {
return (ShrinkViewport() ? TopHeight() : 0) - ContentOffset();
}
float BrowserControls::ContentOffset() {
return top_shown_ratio_ * TopHeight();
}
// Even though this is called *ContentOffset, the value from here isn't used to
// offset the content because only the top controls should do that. For now, the
// BottomContentOffset is the baseline offset when we don't have top controls.
float BrowserControls::BottomContentOffset() {
return bottom_shown_ratio_ * BottomHeight();
}
void BrowserControls::SetShownRatio(float top_ratio, float bottom_ratio) {
// The ratios can be > 1 during height change animations, so we shouldn't
// clamp the values.
top_ratio = std::max(0.f, top_ratio);
bottom_ratio = std::max(0.f, bottom_ratio);
if (top_shown_ratio_ == top_ratio && bottom_shown_ratio_ == bottom_ratio)
return;
top_shown_ratio_ = top_ratio;
bottom_shown_ratio_ = bottom_ratio;
page_->GetChromeClient().DidUpdateBrowserControls();
}
void BrowserControls::UpdateConstraintsAndState(
cc::BrowserControlsState constraints,
cc::BrowserControlsState current) {
permitted_state_ = constraints;
DCHECK(!(constraints == cc::BrowserControlsState::kShown &&
current == cc::BrowserControlsState::kHidden));
DCHECK(!(constraints == cc::BrowserControlsState::kHidden &&
current == cc::BrowserControlsState::kShown));
if (current == cc::BrowserControlsState::kShown) {
top_shown_ratio_ = 1;
bottom_shown_ratio_ = 1;
} else if (current == cc::BrowserControlsState::kHidden) {
top_shown_ratio_ = TopMinShownRatio();
bottom_shown_ratio_ = BottomMinShownRatio();
}
page_->GetChromeClient().DidUpdateBrowserControls();
}
void BrowserControls::SetParams(cc::BrowserControlsParams params) {
if (params_ == params) {
return;
}
params_ = params;
page_->GetChromeClient().DidUpdateBrowserControls();
}
float BrowserControls::TopMinShownRatio() {
return TopHeight() ? params_.top_controls_min_height / TopHeight() : 0.f;
}
float BrowserControls::BottomMinShownRatio() {
return BottomHeight() ? params_.bottom_controls_min_height / BottomHeight()
: 0.f;
}
} // namespace blink