0

[WebLayer] Add WebContentsDelegate::ShouldPinTopControlsToContentTop()

This CL defines WebContentsDelegate::ShouldPinTopControlsToContentTop(),
which embedders can use to tell BrowserControlsOffsetManager to only
show the top browser controls if the page is scrolled to the top.

WebLayer's Browser#setTopView pinToContentTop parameter is hooked up to
this new method.

Bug: 1069498
Change-Id: I7a19656c0ee3219936c3279bfb89591b1f3abb56
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2284138
Commit-Queue: Robbie McElrath <rmcelrath@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Bo <boliu@chromium.org>
Reviewed-by: David Bokan <bokan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#790985}
This commit is contained in:
Robbie McElrath
2020-07-22 20:22:55 +00:00
committed by Commit Bot
parent fa60a76aa5
commit c727f0eb0f
24 changed files with 225 additions and 10 deletions

@@ -7,6 +7,7 @@
#include <stdint.h> #include <stdint.h>
#include <algorithm> #include <algorithm>
#include <utility>
#include "base/check_op.h" #include "base/check_op.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
@@ -369,7 +370,21 @@ gfx::Vector2dF BrowserControlsOffsetManager::ScrollBy(
pending_delta.y() < 0)) pending_delta.y() < 0))
return pending_delta; return pending_delta;
accumulated_scroll_delta_ += pending_delta.y(); // Scroll the page up before expanding the browser controls if
// ShouldPinTopControlsToContentTop() returns true.
float viewport_offset_y = client_->ViewportScrollOffset().y();
if (client_->ShouldPinTopControlsToContentTop() && pending_delta.y() < 0 &&
viewport_offset_y > 0) {
// Reset the baseline so the controls will immediately begin to scroll
// once we're at the top.
ResetBaseline();
// Only scroll the controls by the amount remaining after the page contents
// have been scrolled to the top.
accumulated_scroll_delta_ =
std::min(0.f, pending_delta.y() + viewport_offset_y);
} else {
accumulated_scroll_delta_ += pending_delta.y();
}
// We want to base our calculations on top or bottom controls. After consuming // 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 // the scroll delta, we will calculate a shown ratio for the controls. The

@@ -5,6 +5,10 @@
#ifndef CC_INPUT_BROWSER_CONTROLS_OFFSET_MANAGER_CLIENT_H_ #ifndef CC_INPUT_BROWSER_CONTROLS_OFFSET_MANAGER_CLIENT_H_
#define CC_INPUT_BROWSER_CONTROLS_OFFSET_MANAGER_CLIENT_H_ #define CC_INPUT_BROWSER_CONTROLS_OFFSET_MANAGER_CLIENT_H_
namespace gfx {
class ScrollOffset;
}
namespace cc { namespace cc {
class CC_EXPORT BrowserControlsOffsetManagerClient { class CC_EXPORT BrowserControlsOffsetManagerClient {
@@ -17,7 +21,9 @@ class CC_EXPORT BrowserControlsOffsetManagerClient {
float bottom_ratio) = 0; float bottom_ratio) = 0;
virtual float CurrentTopControlsShownRatio() const = 0; virtual float CurrentTopControlsShownRatio() const = 0;
virtual float CurrentBottomControlsShownRatio() const = 0; virtual float CurrentBottomControlsShownRatio() const = 0;
virtual gfx::ScrollOffset ViewportScrollOffset() const = 0;
virtual void DidChangeBrowserControlsPosition() = 0; virtual void DidChangeBrowserControlsPosition() = 0;
virtual bool ShouldPinTopControlsToContentTop() const = 0;
virtual bool HaveRootScrollNode() const = 0; virtual bool HaveRootScrollNode() const = 0;
virtual void SetNeedsCommit() = 0; virtual void SetNeedsCommit() = 0;

@@ -6,6 +6,7 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <limits>
#include <memory> #include <memory>
#include "base/time/time.h" #include "base/time/time.h"
@@ -30,7 +31,8 @@ class MockBrowserControlsOffsetManagerClient
: host_impl_(&task_runner_provider_, &task_graph_runner_), : host_impl_(&task_runner_provider_, &task_graph_runner_),
redraw_needed_(false), redraw_needed_(false),
update_draw_properties_needed_(false), update_draw_properties_needed_(false),
browser_controls_params_({top_controls_height, 0, 0, 0, false, false}), browser_controls_params_(
{top_controls_height, 0, 0, 0, false, false, false}),
bottom_controls_shown_ratio_(1.f), bottom_controls_shown_ratio_(1.f),
top_controls_shown_ratio_(1.f), top_controls_shown_ratio_(1.f),
browser_controls_show_threshold_(browser_controls_show_threshold), browser_controls_show_threshold_(browser_controls_show_threshold),
@@ -66,6 +68,14 @@ class MockBrowserControlsOffsetManagerClient
return browser_controls_params_.top_controls_min_height; return browser_controls_params_.top_controls_min_height;
} }
bool ShouldPinTopControlsToContentTop() const override {
return browser_controls_params_.pin_top_controls_to_content_top;
}
gfx::ScrollOffset ViewportScrollOffset() const override {
return viewport_scroll_offset_;
}
void SetCurrentBrowserControlsShownRatio(float top_ratio, void SetCurrentBrowserControlsShownRatio(float top_ratio,
float bottom_ratio) override { float bottom_ratio) override {
AssertAndClamp(&top_ratio); AssertAndClamp(&top_ratio);
@@ -110,6 +120,15 @@ class MockBrowserControlsOffsetManagerClient
params.animate_browser_controls_height_changes); params.animate_browser_controls_height_changes);
} }
void SetViewportScrollOffset(float x, float y) {
viewport_scroll_offset_ = gfx::ScrollOffset(x, y);
}
void ScrollVerticallyBy(float dy) {
gfx::Vector2dF viewport_scroll_delta = manager()->ScrollBy({0.f, dy});
viewport_scroll_offset_.Add(gfx::ScrollOffset(viewport_scroll_delta));
}
private: private:
FakeImplTaskRunnerProvider task_runner_provider_; FakeImplTaskRunnerProvider task_runner_provider_;
TestTaskGraphRunner task_graph_runner_; TestTaskGraphRunner task_graph_runner_;
@@ -125,6 +144,7 @@ class MockBrowserControlsOffsetManagerClient
float top_controls_shown_ratio_; float top_controls_shown_ratio_;
float browser_controls_show_threshold_; float browser_controls_show_threshold_;
float browser_controls_hide_threshold_; float browser_controls_hide_threshold_;
gfx::ScrollOffset viewport_scroll_offset_;
}; };
TEST(BrowserControlsOffsetManagerTest, EnsureScrollThresholdApplied) { TEST(BrowserControlsOffsetManagerTest, EnsureScrollThresholdApplied) {
@@ -1143,5 +1163,44 @@ TEST(BrowserControlsOffsetManagerTest,
EXPECT_FLOAT_EQ(1.f, client.CurrentBottomControlsShownRatio()); EXPECT_FLOAT_EQ(1.f, client.CurrentBottomControlsShownRatio());
} }
TEST(BrowserControlsOffsetManagerTest, PinTopControlsToContentTop) {
MockBrowserControlsOffsetManagerClient client(0.f, 0.5f, 0.5f);
client.SetBrowserControlsParams({/*top_controls_height=*/100.f, 0, 0, 0,
false, false,
/*pin_top_controls_to_content_top=*/true});
BrowserControlsOffsetManager* manager = client.manager();
// Scroll down to hide the controls entirely.
manager->ScrollBegin();
client.ScrollVerticallyBy(150.f);
EXPECT_FLOAT_EQ(-100.f, manager->ControlsTopOffset());
EXPECT_FLOAT_EQ(50.f, client.ViewportScrollOffset().y());
manager->ScrollEnd();
manager->ScrollBegin();
// Scroll back up a bit and ensure the controls don't move until we're at
// the top.
client.ScrollVerticallyBy(-20.f);
EXPECT_FLOAT_EQ(-100.f, manager->ControlsTopOffset());
EXPECT_FLOAT_EQ(30.f, client.ViewportScrollOffset().y());
client.ScrollVerticallyBy(-10.f);
EXPECT_FLOAT_EQ(-100.f, manager->ControlsTopOffset());
EXPECT_FLOAT_EQ(20.f, client.ViewportScrollOffset().y());
// After scrolling past the top, the top controls should start showing.
client.ScrollVerticallyBy(-30.f);
EXPECT_FLOAT_EQ(-90.f, manager->ControlsTopOffset());
EXPECT_FLOAT_EQ(0.f, client.ViewportScrollOffset().y());
client.ScrollVerticallyBy(-50.f);
EXPECT_FLOAT_EQ(-40.f, manager->ControlsTopOffset());
// The final offset is greater than gtest's epsilon.
EXPECT_GT(0.0001f, client.ViewportScrollOffset().y());
manager->ScrollEnd();
}
} // namespace } // namespace
} // namespace cc } // namespace cc

@@ -32,6 +32,7 @@ IPC_STRUCT_TRAITS_BEGIN(cc::BrowserControlsParams)
IPC_STRUCT_TRAITS_MEMBER(bottom_controls_min_height) IPC_STRUCT_TRAITS_MEMBER(bottom_controls_min_height)
IPC_STRUCT_TRAITS_MEMBER(animate_browser_controls_height_changes) IPC_STRUCT_TRAITS_MEMBER(animate_browser_controls_height_changes)
IPC_STRUCT_TRAITS_MEMBER(browser_controls_shrink_blink_size) IPC_STRUCT_TRAITS_MEMBER(browser_controls_shrink_blink_size)
IPC_STRUCT_TRAITS_MEMBER(pin_top_controls_to_content_top)
IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_END()
#endif // CC_IPC_CC_PARAM_TRAITS_MACROS_H_ #endif // CC_IPC_CC_PARAM_TRAITS_MACROS_H_

@@ -15,7 +15,9 @@ bool BrowserControlsParams::operator==(
animate_browser_controls_height_changes == animate_browser_controls_height_changes ==
other.animate_browser_controls_height_changes && other.animate_browser_controls_height_changes &&
browser_controls_shrink_blink_size == browser_controls_shrink_blink_size ==
other.browser_controls_shrink_blink_size; other.browser_controls_shrink_blink_size &&
pin_top_controls_to_content_top ==
other.pin_top_controls_to_content_top;
} }
bool BrowserControlsParams::operator!=( bool BrowserControlsParams::operator!=(

@@ -35,6 +35,11 @@ struct CC_EXPORT BrowserControlsParams {
// URL-bar (always false on platforms where URL-bar hiding isn't supported). // URL-bar (always false on platforms where URL-bar hiding isn't supported).
bool browser_controls_shrink_blink_size = false; bool browser_controls_shrink_blink_size = false;
// Whether or not the top controls should be pinned to the top of the page
// contents. If true, collapsed top controls won't begin scrolling into view
// until the page is scrolled to the top.
bool pin_top_controls_to_content_top = false;
bool operator==(const BrowserControlsParams& other) const; bool operator==(const BrowserControlsParams& other) const;
bool operator!=(const BrowserControlsParams& other) const; bool operator!=(const BrowserControlsParams& other) const;
}; };

@@ -3049,6 +3049,10 @@ void LayerTreeHostImpl::DidLoseLayerTreeFrameSink() {
client_->DidLoseLayerTreeFrameSinkOnImplThread(); client_->DidLoseLayerTreeFrameSinkOnImplThread();
} }
bool LayerTreeHostImpl::ShouldPinTopControlsToContentTop() const {
return active_tree_->pin_top_controls_to_content_top();
}
bool LayerTreeHostImpl::HaveRootScrollNode() const { bool LayerTreeHostImpl::HaveRootScrollNode() const {
return InnerViewportScrollNode(); return InnerViewportScrollNode();
} }
@@ -3779,6 +3783,10 @@ float LayerTreeHostImpl::CurrentBottomControlsShownRatio() const {
return active_tree_->CurrentBottomControlsShownRatio(); return active_tree_->CurrentBottomControlsShownRatio();
} }
gfx::ScrollOffset LayerTreeHostImpl::ViewportScrollOffset() const {
return viewport_->TotalScrollOffset();
}
void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) { void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) {
DCHECK(input_handler_client_ == nullptr); DCHECK(input_handler_client_ == nullptr);
input_handler_client_ = client; input_handler_client_ = client;

@@ -345,9 +345,11 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
float bottom_ratio) override; float bottom_ratio) override;
float CurrentTopControlsShownRatio() const override; float CurrentTopControlsShownRatio() const override;
float CurrentBottomControlsShownRatio() const override; float CurrentBottomControlsShownRatio() const override;
gfx::ScrollOffset ViewportScrollOffset() const override;
void DidChangeBrowserControlsPosition() override; void DidChangeBrowserControlsPosition() override;
void DidObserveScrollDelay(base::TimeDelta scroll_delay, void DidObserveScrollDelay(base::TimeDelta scroll_delay,
base::TimeTicks scroll_timestamp); base::TimeTicks scroll_timestamp);
bool ShouldPinTopControlsToContentTop() const override;
bool HaveRootScrollNode() const override; bool HaveRootScrollNode() const override;
void SetNeedsCommit() override; void SetNeedsCommit() override;

@@ -644,6 +644,9 @@ class CC_EXPORT LayerTreeImpl {
float bottom_controls_min_height() const { float bottom_controls_min_height() const {
return browser_controls_params_.bottom_controls_min_height; return browser_controls_params_.bottom_controls_min_height;
} }
bool pin_top_controls_to_content_top() const {
return browser_controls_params_.pin_top_controls_to_content_top;
}
void set_overscroll_behavior(const OverscrollBehavior& behavior); void set_overscroll_behavior(const OverscrollBehavior& behavior);
OverscrollBehavior overscroll_behavior() const { OverscrollBehavior overscroll_behavior() const {

@@ -38,6 +38,10 @@ bool RenderViewHostDelegateView::DoBrowserControlsShrinkRendererSize() const {
return false; return false;
} }
bool RenderViewHostDelegateView::ShouldPinTopControlsToContentTop() const {
return false;
}
void RenderViewHostDelegateView::GestureEventAck( void RenderViewHostDelegateView::GestureEventAck(
const blink::WebGestureEvent& event, const blink::WebGestureEvent& event,
blink::mojom::InputEventResultState ack_result) {} blink::mojom::InputEventResultState ack_result) {}

@@ -93,6 +93,10 @@ class CONTENT_EXPORT RenderViewHostDelegateView {
// Returns true if the browser controls resize the renderer's view size. // Returns true if the browser controls resize the renderer's view size.
virtual bool DoBrowserControlsShrinkRendererSize() const; virtual bool DoBrowserControlsShrinkRendererSize() const;
// Returns true if the top controls should be pinned to the top of the page,
// so they'll only be visible if the page is scrolled to the top.
virtual bool ShouldPinTopControlsToContentTop() const;
// Do post-event tasks for gesture events. // Do post-event tasks for gesture events.
virtual void GestureEventAck(const blink::WebGestureEvent& event, virtual void GestureEventAck(const blink::WebGestureEvent& event,
blink::mojom::InputEventResultState ack_result); blink::mojom::InputEventResultState ack_result);

@@ -871,6 +871,8 @@ blink::VisualProperties RenderWidgetHostImpl::GetVisualProperties() {
visual_properties.browser_controls_params visual_properties.browser_controls_params
.animate_browser_controls_height_changes = .animate_browser_controls_height_changes =
rvh_delegate_view->ShouldAnimateBrowserControlsHeightChanges(); rvh_delegate_view->ShouldAnimateBrowserControlsHeightChanges();
visual_properties.browser_controls_params.pin_top_controls_to_content_top =
rvh_delegate_view->ShouldPinTopControlsToContentTop();
float top_controls_height = rvh_delegate_view->GetTopControlsHeight(); float top_controls_height = rvh_delegate_view->GetTopControlsHeight();
float top_controls_min_height = rvh_delegate_view->GetTopControlsMinHeight(); float top_controls_min_height = rvh_delegate_view->GetTopControlsMinHeight();

@@ -518,6 +518,11 @@ bool WebContentsViewAndroid::DoBrowserControlsShrinkRendererSize() const {
delegate->DoBrowserControlsShrinkRendererSize(web_contents_); delegate->DoBrowserControlsShrinkRendererSize(web_contents_);
} }
bool WebContentsViewAndroid::ShouldPinTopControlsToContentTop() const {
auto* delegate = web_contents_->GetDelegate();
return delegate && delegate->ShouldPinTopControlsToContentTop();
}
bool WebContentsViewAndroid::OnTouchEvent(const ui::MotionEventAndroid& event) { bool WebContentsViewAndroid::OnTouchEvent(const ui::MotionEventAndroid& event) {
if (event.GetAction() == ui::MotionEventAndroid::Action::DOWN && if (event.GetAction() == ui::MotionEventAndroid::Action::DOWN &&
ShouldRequestUnbufferedDispatch()) { ShouldRequestUnbufferedDispatch()) {

@@ -109,6 +109,7 @@ class WebContentsViewAndroid : public WebContentsView,
int GetBottomControlsMinHeight() const override; int GetBottomControlsMinHeight() const override;
bool ShouldAnimateBrowserControlsHeightChanges() const override; bool ShouldAnimateBrowserControlsHeightChanges() const override;
bool DoBrowserControlsShrinkRendererSize() const override; bool DoBrowserControlsShrinkRendererSize() const override;
bool ShouldPinTopControlsToContentTop() const override;
// ui::EventHandlerAndroid implementation. // ui::EventHandlerAndroid implementation.
bool OnTouchEvent(const ui::MotionEventAndroid& event) override; bool OnTouchEvent(const ui::MotionEventAndroid& event) override;

@@ -320,6 +320,10 @@ bool WebContentsDelegate::DoBrowserControlsShrinkRendererSize(
return false; return false;
} }
bool WebContentsDelegate::ShouldPinTopControlsToContentTop() {
return false;
}
PictureInPictureResult WebContentsDelegate::EnterPictureInPicture( PictureInPictureResult WebContentsDelegate::EnterPictureInPicture(
WebContents* web_contents, WebContents* web_contents,
const viz::SurfaceId&, const viz::SurfaceId&,

@@ -643,6 +643,9 @@ class CONTENT_EXPORT WebContentsDelegate {
virtual bool ShouldAnimateBrowserControlsHeightChanges(); virtual bool ShouldAnimateBrowserControlsHeightChanges();
virtual bool DoBrowserControlsShrinkRendererSize( virtual bool DoBrowserControlsShrinkRendererSize(
const WebContents* web_contents); const WebContents* web_contents);
// Returns true if the top controls should be pinned to the top of the page,
// so they'll only be visible if the page is scrolled to the top.
virtual bool ShouldPinTopControlsToContentTop();
// Propagates to the browser that gesture scrolling has changed state. This is // Propagates to the browser that gesture scrolling has changed state. This is
// used by the browser to assist in controlling the behavior of sliding the // used by the browser to assist in controlling the behavior of sliding the

@@ -278,6 +278,46 @@ public class BrowserControlsTest {
}); });
} }
// Disabled on L bots due to unexplained flakes. See crbug.com/1035894.
@MinAndroidSdkLevel(Build.VERSION_CODES.M)
@Test
@SmallTest
public void testPinTopControlsToContentTop() throws Exception {
InstrumentationActivity activity = mActivityTestRule.getActivity();
View topContents = activity.getTopContentsContainer();
TestThreadUtils.runOnUiThreadBlocking(
() -> activity.getBrowser().setTopView(topContents, 0, /*pinToContentTop=*/true));
// Scroll down past the top-controls, which should collapse the top-controls and change the
// page height.
EventUtils.simulateDragFromCenterOfView(
activity.getWindow().getDecorView(), 0, -2 * mTopViewHeight);
CriteriaHelper.pollInstrumentationThread(() -> {
Criteria.checkThat(
getVisiblePageHeight(), Matchers.greaterThan(mPageHeightWithTopView));
Criteria.checkThat(activity.getTopContentsContainer().getVisibility(),
Matchers.is(View.INVISIBLE));
});
// Scroll part of the way up again, which should not show the top controls.
int scrolledPageHeight = getVisiblePageHeight();
EventUtils.simulateDragFromCenterOfView(
activity.getWindow().getDecorView(), 0, mTopViewHeight);
CriteriaHelper.pollInstrumentationThread(() -> {
Criteria.checkThat(getVisiblePageHeight(), Matchers.is(scrolledPageHeight));
});
// Scroll to the top to show the top controls.
EventUtils.simulateDragFromCenterOfView(
activity.getWindow().getDecorView(), 0, 2 * mTopViewHeight);
CriteriaHelper.pollInstrumentationThread(() -> {
Criteria.checkThat(getVisiblePageHeight(), Matchers.is(mPageHeightWithTopView));
Criteria.checkThat(
activity.getTopContentsContainer().getVisibility(), Matchers.is(View.VISIBLE));
Criteria.checkThat(topContents.getTranslationY(), Matchers.is(0.f));
});
}
/** /**
* Makes sure that the top controls are shown when a js dialog is shown. * Makes sure that the top controls are shown when a js dialog is shown.
* *

@@ -199,6 +199,7 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan
getViewController().setTopView(ObjectWrapper.unwrap(viewWrapper, View.class)); getViewController().setTopView(ObjectWrapper.unwrap(viewWrapper, View.class));
getViewController().setTopControlsMinHeight(minHeight); getViewController().setTopControlsMinHeight(minHeight);
getViewController().setPinTopControlsToContentTop(pinToContentTop);
} }
@Override @Override

@@ -57,6 +57,7 @@ public final class BrowserViewController
private final ModalDialogManager mModalDialogManager; private final ModalDialogManager mModalDialogManager;
private int mTopControlsMinHeight; private int mTopControlsMinHeight;
private boolean mPinToContentTop;
private TabImpl mTab; private TabImpl mTab;
@@ -210,6 +211,11 @@ public final class BrowserViewController
updateActiveTabScrollBehavior(); updateActiveTabScrollBehavior();
} }
public void setPinTopControlsToContentTop(boolean pinToContentTop) {
mPinToContentTop = pinToContentTop;
updateActiveTabScrollBehavior();
}
public void setBottomView(View view) { public void setBottomView(View view) {
mBottomControlsContainerView.setView(view); mBottomControlsContainerView.setView(view);
} }
@@ -303,6 +309,7 @@ public final class BrowserViewController
private void updateActiveTabScrollBehavior() { private void updateActiveTabScrollBehavior() {
if (mTab != null) { if (mTab != null) {
mTab.setTopControlsMinHeight(mTopControlsMinHeight); mTab.setTopControlsMinHeight(mTopControlsMinHeight);
mTab.setPinTopControlsToContentTop(mPinToContentTop);
} }
} }

@@ -871,6 +871,10 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
TabImplJni.get().setTopControlsMinHeight(mNativeTab, minHeight); TabImplJni.get().setTopControlsMinHeight(mNativeTab, minHeight);
} }
/* package */ void setPinTopControlsToContentTop(boolean pinToContentTop) {
TabImplJni.get().setPinTopControlsToContentTop(mNativeTab, pinToContentTop);
}
@CalledByNative @CalledByNative
private boolean doBrowserControlsShrinkRendererSize() { private boolean doBrowserControlsShrinkRendererSize() {
BrowserViewController viewController = getViewController(); BrowserViewController viewController = getViewController();
@@ -993,5 +997,6 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
boolean canTranslate(long nativeTabImpl); boolean canTranslate(long nativeTabImpl);
void showTranslateUi(long nativeTabImpl); void showTranslateUi(long nativeTabImpl);
void setTopControlsMinHeight(long nativeTabImpl, int minHeight); void setTopControlsMinHeight(long nativeTabImpl, int minHeight);
void setPinTopControlsToContentTop(long nativeTabImpl, boolean pinToContentTop);
} }
} }

@@ -780,6 +780,12 @@ void TabImpl::ShowTranslateUi(JNIEnv* env) {
void TabImpl::SetTopControlsMinHeight(JNIEnv* env, int min_height) { void TabImpl::SetTopControlsMinHeight(JNIEnv* env, int min_height) {
top_controls_min_height_ = min_height; top_controls_min_height_ = min_height;
} }
void TabImpl::SetPinTopControlsToContentTop(
JNIEnv* env,
jboolean pin_top_controls_to_content_top) {
pin_top_controls_to_content_top_ = pin_top_controls_to_content_top;
}
#endif // OS_ANDROID #endif // OS_ANDROID
content::WebContents* TabImpl::OpenURLFromTab( content::WebContents* TabImpl::OpenURLFromTab(
@@ -920,6 +926,14 @@ bool TabImpl::DoBrowserControlsShrinkRendererSize(
#endif #endif
} }
bool TabImpl::ShouldPinTopControlsToContentTop() {
#if defined(OS_ANDROID)
return pin_top_controls_to_content_top_;
#else
return false;
#endif
}
bool TabImpl::EmbedsFullscreenWidget() { bool TabImpl::EmbedsFullscreenWidget() {
return true; return true;
} }

@@ -194,6 +194,8 @@ class TabImpl : public Tab,
jboolean CanTranslate(JNIEnv* env); jboolean CanTranslate(JNIEnv* env);
void ShowTranslateUi(JNIEnv* env); void ShowTranslateUi(JNIEnv* env);
void SetTopControlsMinHeight(JNIEnv* env, int min_height); void SetTopControlsMinHeight(JNIEnv* env, int min_height);
void SetPinTopControlsToContentTop(JNIEnv* env,
jboolean pin_top_controls_to_content_top);
#endif #endif
ErrorPageDelegate* error_page_delegate() { return error_page_delegate_; } ErrorPageDelegate* error_page_delegate() { return error_page_delegate_; }
@@ -262,6 +264,7 @@ class TabImpl : public Tab,
int GetBottomControlsHeight() override; int GetBottomControlsHeight() override;
bool DoBrowserControlsShrinkRendererSize( bool DoBrowserControlsShrinkRendererSize(
const content::WebContents* web_contents) override; const content::WebContents* web_contents) override;
bool ShouldPinTopControlsToContentTop() override;
bool EmbedsFullscreenWidget() override; bool EmbedsFullscreenWidget() override;
void RequestMediaAccessPermission( void RequestMediaAccessPermission(
content::WebContents* web_contents, content::WebContents* web_contents,
@@ -367,6 +370,7 @@ class TabImpl : public Tab,
std::unique_ptr<BrowserControlsNavigationStateHandler> std::unique_ptr<BrowserControlsNavigationStateHandler>
browser_controls_navigation_state_handler_; browser_controls_navigation_state_handler_;
int top_controls_min_height_ = 0; int top_controls_min_height_ = 0;
bool pin_top_controls_to_content_top_ = false;
// Last value supplied to UpdateBrowserControlsConstraint(). This *constraint* // Last value supplied to UpdateBrowserControlsConstraint(). This *constraint*
// can be SHOWN, if for example a modal dialog is forcing the controls to be // can be SHOWN, if for example a modal dialog is forcing the controls to be

@@ -15,9 +15,16 @@
<item android:id="@+id/toggle_bottom_view_id" <item android:id="@+id/toggle_bottom_view_id"
android:checkable="true" android:checkable="true"
android:title="Bottom view" /> android:title="Bottom view" />
<item android:id="@+id/toggle_top_view_min_height_id" <item android:title="Scrolling">
android:checkable="true" <menu xmlns:android="http://schemas.android.com/apk/res/android">
android:title="Set top view min height" /> <item android:id="@+id/toggle_top_view_min_height_id"
android:checkable="true"
android:title="Set top view min height" />
<item android:id="@+id/toggle_top_view_pinned_to_top_id"
android:checkable="true"
android:title="Pin top view to content top" />
</menu>
</item>
<item android:id="@+id/site_settings_menu_id" <item android:id="@+id/site_settings_menu_id"
android:title="Site Settings" /> android:title="Site Settings" />
<item android:id="@+id/translate_menu_id" <item android:id="@+id/translate_menu_id"

@@ -140,6 +140,7 @@ public class WebLayerShellActivity extends FragmentActivity {
private Runnable mExitFullscreenRunnable; private Runnable mExitFullscreenRunnable;
private View mBottomView; private View mBottomView;
private int mTopViewMinHeight; private int mTopViewMinHeight;
private boolean mTopViewPinnedToContentTop;
private boolean mInIncognitoMode; private boolean mInIncognitoMode;
@Override @Override
@@ -181,8 +182,12 @@ public class WebLayerShellActivity extends FragmentActivity {
popup.getMenuInflater().inflate(R.menu.app_menu, popup.getMenu()); popup.getMenuInflater().inflate(R.menu.app_menu, popup.getMenu());
MenuItem bottomMenuItem = popup.getMenu().findItem(R.id.toggle_bottom_view_id); MenuItem bottomMenuItem = popup.getMenu().findItem(R.id.toggle_bottom_view_id);
bottomMenuItem.setChecked(mBottomView != null); bottomMenuItem.setChecked(mBottomView != null);
MenuItem topMenuItem = popup.getMenu().findItem(R.id.toggle_top_view_min_height_id); popup.getMenu()
topMenuItem.setChecked(mTopViewMinHeight > 0); .findItem(R.id.toggle_top_view_min_height_id)
.setChecked(mTopViewMinHeight > 0);
popup.getMenu()
.findItem(R.id.toggle_top_view_pinned_to_top_id)
.setChecked(mTopViewPinnedToContentTop);
popup.getMenu() popup.getMenu()
.findItem(R.id.translate_menu_id) .findItem(R.id.translate_menu_id)
.setVisible(mBrowser.getActiveTab().canTranslate()); .setVisible(mBrowser.getActiveTab().canTranslate());
@@ -219,7 +224,15 @@ public class WebLayerShellActivity extends FragmentActivity {
if (item.getItemId() == R.id.toggle_top_view_min_height_id) { if (item.getItemId() == R.id.toggle_top_view_min_height_id) {
mTopViewMinHeight = (mTopViewMinHeight == 0) ? 50 : 0; mTopViewMinHeight = (mTopViewMinHeight == 0) ? 50 : 0;
mBrowser.setTopView(mTopContentsContainer, mTopViewMinHeight, false); mBrowser.setTopView(
mTopContentsContainer, mTopViewMinHeight, mTopViewPinnedToContentTop);
return true;
}
if (item.getItemId() == R.id.toggle_top_view_pinned_to_top_id) {
mTopViewPinnedToContentTop = !mTopViewPinnedToContentTop;
mBrowser.setTopView(
mTopContentsContainer, mTopViewMinHeight, mTopViewPinnedToContentTop);
return true; return true;
} }
@@ -294,7 +307,7 @@ public class WebLayerShellActivity extends FragmentActivity {
mProfile.setBooleanSetting(SettingType.UKM_ENABLED, true); mProfile.setBooleanSetting(SettingType.UKM_ENABLED, true);
setTabCallbacks(mBrowser.getActiveTab(), fragment); setTabCallbacks(mBrowser.getActiveTab(), fragment);
mBrowser.setTopView(mTopContentsContainer); mBrowser.setTopView(mTopContentsContainer, /*minHeight=*/0, /*pinToContentTop=*/false);
mTabListCallback = new TabListCallback() { mTabListCallback = new TabListCallback() {
@Override @Override
public void onActiveTabChanged(Tab activeTab) { public void onActiveTabChanged(Tab activeTab) {