diff --git a/build/patches/Move-toptoolbar-to-bottom.patch b/build/patches/Move-toptoolbar-to-bottom.patch new file mode 100644 index 00000000..5fe1b2ab --- /dev/null +++ b/build/patches/Move-toptoolbar-to-bottom.patch @@ -0,0 +1,2172 @@ +From: uazo +Date: Tue, 18 Jan 2022 07:43:32 +0000 +Subject: Move top toolbar to bottom + +Adds an accessibility flag that allows the top toolbar to be moved to the bottom. +The patch also includes tablet mode. +--- + cc/base/features.cc | 3 ++ + cc/base/features.h | 1 + + cc/input/browser_controls_offset_manager.cc | 6 +++ + cc/trees/layer_tree_host_impl.cc | 3 ++ + .../tab_management/TabGroupUiCoordinator.java | 7 ++- + .../tab_management/TabGroupUiMediator.java | 50 +++++++++++++++++- + .../tab_management/TabGroupUiProperties.java | 6 ++- + .../tab_management/TabGroupUiToolbarView.java | 18 +++++++ + .../tab_management/TabGroupUiViewBinder.java | 3 ++ + .../tab_management/TabSwitcherMediator.java | 15 ++++++ + .../ChromeAccessibilitySettingsDelegate.java | 52 +++++++++++++++++++ + .../chrome/browser/app/ChromeActivity.java | 8 +++ + .../browser/app/flags/ChromeCachedFlags.java | 1 + + .../compositor/CompositorViewHolder.java | 6 +++ + .../layouts/LayoutManagerChromeTablet.java | 4 +- + .../layouts/ToolbarSwipeLayout.java | 15 +++++- + .../overlays/strip/StripLayoutHelper.java | 2 +- + .../strip/StripLayoutHelperManager.java | 35 +++++++++++-- + .../scene_layer/StaticTabSceneLayer.java | 8 ++- + .../scene_layer/TabListSceneLayer.java | 14 +++++ + .../scene_layer/TabStripSceneLayer.java | 15 +++++- + .../browser/findinpage/FindToolbarTablet.java | 11 ++-- + .../fullscreen/BrowserControlsManager.java | 13 +++++ + .../messages/MessageContainerCoordinator.java | 17 +++++- + .../modaldialog/ChromeTabModalPresenter.java | 2 +- + .../chrome/browser/ntp/NewTabPage.java | 13 +++-- + .../chrome/browser/ntp/RecentTabsPage.java | 22 ++++++-- + .../browser/searchwidget/SearchActivity.java | 12 ++++- + .../browser/settings/SettingsActivity.java | 5 ++ + .../StatusIndicatorCoordinator.java | 10 ++++ + .../StatusIndicatorSceneLayer.java | 7 ++- + .../browser/toolbar/ToolbarManager.java | 37 +++++++++++-- + .../chrome/browser/ui/BottomContainer.java | 19 +++++++ + chrome/browser/about_flags.cc | 5 ++ + .../scene_layer/tab_strip_scene_layer.cc | 16 ++++-- + .../BrowserControlsMarginSupplier.java | 6 +++ + .../BrowserControlsStateProvider.java | 6 +++ + chrome/browser/flag_descriptions.cc | 4 ++ + chrome/browser/flag_descriptions.h | 3 ++ + .../flags/android/cached_feature_flags.cc | 18 +++++++ + .../flags/android/chrome_feature_list.cc | 2 + + .../browser/flags/CachedFeatureFlags.java | 19 +++++++ + .../browser/flags/ChromeFeatureList.java | 2 + + .../chrome/browser/ui/appmenu/AppMenu.java | 4 ++ + .../omnibox/LocationBarCoordinator.java | 9 +++- + .../browser/omnibox/UrlBarCoordinator.java | 11 +++- + .../suggestions/AutocompleteCoordinator.java | 16 +++++- + .../OmniboxSuggestionsDropdown.java | 23 +++++++- + .../OmniboxSuggestionsDropdownEmbedder.java | 4 ++ + .../strings/android_chrome_strings.grd | 6 +++ + chrome/browser/ui/android/toolbar/BUILD.gn | 1 + + .../toolbar/LocationBarFocusScrimHandler.java | 7 +++ + .../bottom/BottomControlsContentDelegate.java | 9 +++- + .../bottom/BottomControlsCoordinator.java | 11 +++- + .../bottom/BottomControlsMediator.java | 9 ++++ + .../bottom/BottomControlsProperties.java | 5 +- + .../bottom/BottomControlsViewBinder.java | 2 + + .../bottom/ScrollingBottomViewSceneLayer.java | 20 ++++++- + .../toolbar/top/ToolbarControlContainer.java | 11 ++++ + .../top/TopToolbarOverlayCoordinator.java | 7 +++ + .../top/TopToolbarOverlayProperties.java | 8 ++- + .../toolbar/top/TopToolbarSceneLayer.java | 11 +++- + .../res/xml/accessibility_preferences.xml | 4 ++ + .../accessibility/AccessibilitySettings.java | 16 ++++++ + .../AccessibilitySettingsDelegate.java | 6 +++ + .../render_widget_host_view_android.cc | 3 ++ + 66 files changed, 673 insertions(+), 51 deletions(-) + +diff --git a/cc/base/features.cc b/cc/base/features.cc +--- a/cc/base/features.cc ++++ b/cc/base/features.cc +@@ -39,6 +39,9 @@ const base::Feature kSynchronizedScrolling = { + base::FEATURE_ENABLED_BY_DEFAULT}; + #endif + ++const base::Feature kMoveTopToolbarToBottom = { ++ "MoveTopToolbarToBottom", base::FEATURE_DISABLED_BY_DEFAULT}; ++ + const base::Feature kRemoveMobileViewportDoubleTap{ + "RemoveMobileViewportDoubleTap", base::FEATURE_ENABLED_BY_DEFAULT}; + +diff --git a/cc/base/features.h b/cc/base/features.h +--- a/cc/base/features.h ++++ b/cc/base/features.h +@@ -14,6 +14,7 @@ namespace features { + CC_BASE_EXPORT extern const base::Feature kAnimatedImageResume; + CC_BASE_EXPORT extern const base::Feature kImpulseScrollAnimations; + CC_BASE_EXPORT extern const base::Feature kSynchronizedScrolling; ++CC_BASE_EXPORT extern const base::Feature kMoveTopToolbarToBottom; + + // When enabled, the double tap to zoom will be disabled when the viewport + // meta tag is properly set for mobile using content=width=device-width +diff --git a/cc/input/browser_controls_offset_manager.cc b/cc/input/browser_controls_offset_manager.cc +--- a/cc/input/browser_controls_offset_manager.cc ++++ b/cc/input/browser_controls_offset_manager.cc +@@ -18,6 +18,7 @@ + #include "ui/gfx/animation/tween.h" + #include "ui/gfx/geometry/transform.h" + #include "ui/gfx/geometry/vector2d_f.h" ++#include "cc/base/features.h" + + namespace cc { + namespace { +@@ -469,6 +470,11 @@ gfx::Vector2dF BrowserControlsOffsetManager::ScrollBy( + // content. If the top controls have no height, the content should scroll + // immediately. + gfx::Vector2dF applied_delta(0.f, old_top_offset - ContentTopOffset()); ++ // do not eat scroll offsets if the flag is on, since the content view ++ // top offsets are not changed. It is necessary to synchronize the scroll ++ // with the offset of the user's movement ++ if (base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) ++ return pending_delta; + return pending_delta - applied_delta; + } + +diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc +--- a/cc/trees/layer_tree_host_impl.cc ++++ b/cc/trees/layer_tree_host_impl.cc +@@ -4236,6 +4236,9 @@ bool LayerTreeHostImpl::AnimateBrowserControls(base::TimeTicks time) { + if (scroll_delta.IsZero()) + return false; + ++ if (base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) ++ return false; ++ + // This counter-scrolls the page to keep the appearance of the page content + // being fixed while the browser controls animate. + viewport().ScrollBy(scroll_delta, +diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java +--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java ++++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java +@@ -44,6 +44,7 @@ import org.chromium.components.feature_engagement.FeatureConstants; + import org.chromium.ui.modelutil.PropertyModel; + import org.chromium.ui.modelutil.PropertyModelChangeProcessor; + import org.chromium.ui.resources.dynamics.DynamicResourceLoader; ++import org.chromium.chrome.browser.theme.TopUiThemeColorProvider; + + import java.util.List; + +@@ -126,7 +127,8 @@ public class TabGroupUiCoordinator implements TabGroupUiMediator.ResetHandler, T + */ + @Override + public void initializeWithNative(Activity activity, +- BottomControlsCoordinator.BottomControlsVisibilityController visibilityController) { ++ BottomControlsCoordinator.BottomControlsVisibilityController visibilityController, ++ TopUiThemeColorProvider topUiThemeColorProvider, ObservableSupplier tabSupplier) { + if (UmaSessionStats.isMetricsServiceAvailable()) { + UmaSessionStats.registerSyntheticFieldTrial( + ChromeFeatureList.TAB_GROUPS_ANDROID + SYNTHETIC_TRIAL_POSTFIX, +@@ -162,7 +164,8 @@ public class TabGroupUiCoordinator implements TabGroupUiMediator.ResetHandler, T + mMediator = new TabGroupUiMediator(mActivity, visibilityController, this, mModel, + mTabModelSelector, mTabCreatorManager, mOverviewModeBehaviorSupplier, + mIncognitoStateProvider, dialogController, mActivityLifecycleDispatcher, +- mSnackbarManager, mOmniboxFocusStateSupplier); ++ mSnackbarManager, mOmniboxFocusStateSupplier, ++ topUiThemeColorProvider, tabSupplier); + + TabGroupUtils.startObservingForCreationIPH(); + +diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java +--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java ++++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java +@@ -57,6 +57,12 @@ import org.chromium.url.GURL; + import java.util.ArrayList; + import java.util.List; + ++import org.chromium.chrome.browser.theme.TopUiThemeColorProvider; ++import org.chromium.chrome.browser.tab.CurrentTabObserver; ++import org.chromium.chrome.browser.tab.EmptyTabObserver; ++import org.chromium.chrome.browser.tab.Tab; ++import androidx.annotation.ColorInt; ++ + /** + * A mediator for the TabGroupUi. Responsible for managing the internal state of the component. + */ +@@ -129,6 +135,11 @@ public class TabGroupUiMediator implements SnackbarManager.SnackbarController { + private boolean mIsShowingOverViewMode; + private boolean mActivatedButNotShown; + ++ private final TopUiThemeColorProvider mTopUiThemeColorProvider; ++ ++ /** An observer that watches for changes in the active tab. */ ++ private final CurrentTabObserver mTabObserver; ++ + TabGroupUiMediator(Context context, + BottomControlsCoordinator.BottomControlsVisibilityController visibilityController, + ResetHandler resetHandler, PropertyModel model, TabModelSelector tabModelSelector, +@@ -138,7 +149,9 @@ public class TabGroupUiMediator implements SnackbarManager.SnackbarController { + @Nullable TabGridDialogMediator.DialogController dialogController, + ActivityLifecycleDispatcher activityLifecycleDispatcher, + SnackbarManager snackbarManager, +- ObservableSupplier omniboxFocusStateSupplier) { ++ ObservableSupplier omniboxFocusStateSupplier, ++ TopUiThemeColorProvider topUiThemeColorProvider, ObservableSupplier tabSupplier) { ++ mTopUiThemeColorProvider = topUiThemeColorProvider; + mContext = context; + mResetHandler = resetHandler; + mModel = model; +@@ -165,6 +178,24 @@ public class TabGroupUiMediator implements SnackbarManager.SnackbarController { + mIsShowingOverViewMode = true; + } + ++ // Keep an observer attached to the visible tab (and only the visible tab) to update ++ // properties including theme color. ++ Callback activityTabCallback = (tab) -> { ++ if (tab == null) return; ++ updateThemeColor(tab); ++ }; ++ mTabObserver = new CurrentTabObserver(tabSupplier, new EmptyTabObserver() { ++ @Override ++ public void onDidChangeThemeColor(Tab tab, int color) { ++ updateThemeColor(tab); ++ } ++ ++ @Override ++ public void onContentChanged(Tab tab) { ++ updateThemeColor(tab); ++ } ++ }, activityTabCallback); ++ + // register for tab model + mTabModelObserver = new TabModelObserver() { + private int mAddedTabId = Tab.INVALID_TAB_ID; +@@ -189,6 +220,7 @@ public class TabGroupUiMediator implements SnackbarManager.SnackbarController { + maybeActivateConditionalTabStrip(ReasonToShow.TAB_SWITCHED); + } + } ++ updateThemeColor(tab); + if (type == TabSelectionType.FROM_CLOSE) return; + if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(mContext) + && getTabsToShowForId(lastId).contains(tab)) { +@@ -259,6 +291,7 @@ public class TabGroupUiMediator implements SnackbarManager.SnackbarController { + resetTabStripWithRelatedTabsForId(currentTab.getId()); + RecordUserAction.record("TabStrip.SessionVisibility." + + (mIsTabGroupUiVisible ? "Visible" : "Hidden")); ++ updateThemeColor(currentTab); + } + + @Override +@@ -390,6 +423,20 @@ public class TabGroupUiMediator implements SnackbarManager.SnackbarController { + if (tab != null) { + resetTabStripWithRelatedTabsForId(tab.getId()); + } ++ ++ mTabObserver.triggerWithCurrentTab(); ++ } ++ ++ /** ++ * Update the colors of the layer based on the specified tab. ++ * @param tab The tab to base the colors on. ++ */ ++ private void updateThemeColor(Tab tab) { ++ if (tab != null) { ++ @ColorInt ++ int color = mTopUiThemeColorProvider.getSceneLayerBackground(tab); ++ mModel.set(TabGroupUiProperties.PRIMARY_COLOR, color); ++ } + } + + void setupLeftButtonDrawable(int drawableId) { +@@ -536,6 +583,7 @@ public class TabGroupUiMediator implements SnackbarManager.SnackbarController { + } + + public void destroy() { ++ mTabObserver.destroy(); + if (mTabModelSelector != null) { + mTabModelSelector.getTabModelFilterProvider().removeTabModelFilterObserver( + mTabModelObserver); +diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java +--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java ++++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java +@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.tasks.tab_management; + import android.view.View.OnClickListener; + import org.chromium.ui.modelutil.PropertyKey; + import org.chromium.ui.modelutil.PropertyModel; ++import android.content.res.ColorStateList; + + /** + * {@link PropertyKey} list for the TabGroupUi. +@@ -37,8 +38,11 @@ class TabGroupUiProperties { + .WritableObjectPropertyKey RIGHT_BUTTON_CONTENT_DESCRIPTION = + new PropertyModel.WritableObjectPropertyKey<>(); + ++ public static final PropertyModel.WritableIntPropertyKey PRIMARY_COLOR = ++ new PropertyModel.WritableIntPropertyKey(); ++ + public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {LEFT_BUTTON_ON_CLICK_LISTENER, + RIGHT_BUTTON_ON_CLICK_LISTENER, IS_MAIN_CONTENT_VISIBLE, IS_INCOGNITO, + LEFT_BUTTON_DRAWABLE_ID, INITIAL_SCROLL_INDEX, LEFT_BUTTON_CONTENT_DESCRIPTION, +- RIGHT_BUTTON_CONTENT_DESCRIPTION}; ++ RIGHT_BUTTON_CONTENT_DESCRIPTION, PRIMARY_COLOR}; + } +diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java +--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java ++++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java +@@ -29,6 +29,11 @@ import org.chromium.components.browser_ui.styles.SemanticColorUtils; + import org.chromium.ui.KeyboardVisibilityDelegate; + import org.chromium.ui.widget.ChromeImageView; + ++import org.chromium.ui.util.ColorUtils; ++import org.chromium.chrome.browser.theme.ThemeUtils; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** + * Represents a generic toolbar used in the bottom strip/grid component. + * {@link TabGridPanelToolbarCoordinator} +@@ -171,6 +176,19 @@ public class TabGroupUiToolbarView extends FrameLayout { + mFadingEdgeEnd.setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + ++ void setPrimaryColorAndApplyTint(int color) { ++ if (!CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) ++ return; ++ ++ // change the background color of the bottom bar if the top toolbar is below ++ setPrimaryColor(color); ++ ++ // and adjust the tint ++ boolean useLightTint = ColorUtils.shouldUseLightForegroundOnBackground(color); ++ ColorStateList tint = ThemeUtils.getThemedToolbarIconTint(getContext(), useLightTint); ++ setTint(tint); ++ } ++ + void setTint(ColorStateList tint) { + ApiCompatibilityUtils.setImageTintList(mLeftButton, tint); + ApiCompatibilityUtils.setImageTintList(mRightButton, tint); +diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java +--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java ++++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java +@@ -12,6 +12,7 @@ import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiPropert + import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.LEFT_BUTTON_ON_CLICK_LISTENER; + import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.RIGHT_BUTTON_CONTENT_DESCRIPTION; + import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.RIGHT_BUTTON_ON_CLICK_LISTENER; ++import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.PRIMARY_COLOR; + + import android.view.View; + +@@ -71,6 +72,8 @@ class TabGroupUiViewBinder { + } else if (RIGHT_BUTTON_CONTENT_DESCRIPTION == propertyKey) { + viewHolder.toolbarView.setRightButtonContentDescription( + model.get(RIGHT_BUTTON_CONTENT_DESCRIPTION)); ++ } else if (PRIMARY_COLOR == propertyKey) { ++ viewHolder.toolbarView.setPrimaryColorAndApplyTint(model.get(PRIMARY_COLOR)); + } + } + } +diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java +--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java ++++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java +@@ -410,11 +410,22 @@ class TabSwitcherMediator implements TabSwitcher.Controller, TabListRecyclerView + updateTopControlsProperties(); + mContainerViewModel.set( + BOTTOM_CONTROLS_HEIGHT, browserControlsStateProvider.getBottomControlsHeight()); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ mContainerViewModel.set( ++ BOTTOM_CONTROLS_HEIGHT, mContainerViewModel.get(BOTTOM_CONTROLS_HEIGHT) + ++ mBrowserControlsStateProvider.getContentOffset()); ++ } + } + + if (mMode == TabListMode.GRID) { + mContainerViewModel.set(BOTTOM_PADDING, + (int) context.getResources().getDimension(R.dimen.tab_grid_bottom_padding)); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // adjust the bottom margin so as not to cover the top toolbar at the bottom ++ mContainerViewModel.set( ++ BOTTOM_PADDING, mContainerViewModel.get(BOTTOM_PADDING) + ++ mBrowserControlsStateProvider.getContentOffset()); ++ } + } + + mContainerView = containerView; +@@ -508,6 +519,10 @@ class TabSwitcherMediator implements TabSwitcher.Controller, TabListRecyclerView + final int contentOffset = mBrowserControlsStateProvider.getContentOffset(); + + mContainerViewModel.set(TOP_MARGIN, contentOffset); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // moves the view up since the toolbar is at the bottom ++ mContainerViewModel.set(TOP_MARGIN, 0); ++ } + mContainerViewModel.set(SHADOW_TOP_OFFSET, contentOffset); + } + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java +@@ -18,6 +18,14 @@ import org.chromium.components.browser_ui.accessibility.AccessibilitySettingsDel + import org.chromium.components.user_prefs.UserPrefs; + import org.chromium.content_public.browser.BrowserContextHandle; + ++import android.app.Activity; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; ++import org.chromium.chrome.browser.ui.messages.snackbar.INeedSnackbarManager; ++import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar; ++import org.chromium.chrome.browser.ApplicationLifetime; ++ + /** The Chrome implementation of AccessibilitySettingsDelegate. */ + public class ChromeAccessibilitySettingsDelegate implements AccessibilitySettingsDelegate { + private static final String READER_MODE_SELECTED_HISTOGRAM = +@@ -34,6 +42,12 @@ public class ChromeAccessibilitySettingsDelegate implements AccessibilitySetting + public void setEnabled(boolean value) {} + } + ++ private SnackbarManager mSnackbarManager; ++ ++ public void setSnackbarManager(SnackbarManager snackbarManager) { ++ mSnackbarManager = snackbarManager; ++ } ++ + private static class ReaderForAccessibilityDelegate implements BooleanPreferenceDelegate { + @Override + public boolean isEnabled() { +@@ -67,6 +81,44 @@ public class ChromeAccessibilitySettingsDelegate implements AccessibilitySetting + return new ReaderForAccessibilityDelegate(); + } + ++ private static class MoveTopToolbarToBottomDelegate implements BooleanPreferenceDelegate { ++ @Override ++ public boolean isEnabled() { ++ return CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM); ++ } ++ ++ @Override ++ public void setEnabled(boolean value) { ++ CachedFeatureFlags.setFlagEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM, ++ "move-top-toolbar-to-bottom", value); ++ } ++ } ++ ++ @Override ++ public BooleanPreferenceDelegate getMoveTopToolbarToBottomDelegate() { ++ return new MoveTopToolbarToBottomDelegate(); ++ } ++ ++ @Override ++ public void requestRestart(Activity activity) { ++ Snackbar mSnackbar = Snackbar.make(activity.getString(R.string.ui_relaunch_notice), ++ new SnackbarManager.SnackbarController() { ++ @Override ++ public void onDismissNoAction(Object actionData) { } ++ ++ @Override ++ public void onAction(Object actionData) { ++ ApplicationLifetime.terminate(true); ++ } ++ }, Snackbar.TYPE_NOTIFICATION, Snackbar.UMA_UNKNOWN) ++ .setSingleLine(false) ++ .setAction(activity.getString(R.string.relaunch), ++ /*actionData*/null) ++ .setDuration(/*durationMs*/70000); ++ if (!mSnackbarManager.isShowing()) ++ mSnackbarManager.showSnackbar(mSnackbar); ++ } ++ + private static class ForceTabletUIDelegate implements BooleanPreferenceDelegate { + @Override + public boolean isEnabled() { +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java +@@ -775,6 +775,14 @@ public abstract class ChromeActivity + ImageView shadowImage = findViewById(R.id.toolbar_shadow); + if (shadowImage == null) return; + ++ // Invert the shadown if the top toolbar is at the bottom ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams)shadowImage.getLayoutParams(); ++ marginParams.setMargins(marginParams.leftMargin, 0, ++ marginParams.rightMargin, marginParams.bottomMargin); ++ shadowImage.setLayoutParams(marginParams); ++ } ++ + Drawable drawable = getDrawable(getToolbarShadowResource()); + shadowImage.setImageDrawable(drawable); + LayoutParams layoutParams = shadowImage.getLayoutParams(); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java +@@ -98,6 +98,7 @@ public class ChromeCachedFlags { + add(ChromeFeatureList.INSTANT_START); + add(ChromeFeatureList.INSTANCE_SWITCHER); + add(ChromeFeatureList.INTEREST_FEED_V2); ++ add(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM); + add(ChromeFeatureList.NEW_WINDOW_APP_MENU); + add(ChromeFeatureList.OPTIMIZATION_GUIDE_PUSH_NOTIFICATIONS); + add(ChromeFeatureList.PAINT_PREVIEW_DEMO); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java +@@ -77,6 +77,8 @@ import org.chromium.ui.base.EventOffsetHandler; + import org.chromium.ui.base.WindowAndroid; + import org.chromium.ui.resources.ResourceManager; + import org.chromium.ui.resources.dynamics.DynamicResourceLoader; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + import java.util.ArrayList; + import java.util.HashSet; +@@ -295,6 +297,10 @@ public class CompositorViewHolder extends FrameLayout + WebContents webContents = mTabVisible.getWebContents(); + if (webContents == null) return; + EventForwarder forwarder = webContents.getEventForwarder(); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // no need to adjust the touch offsets, since the content view is never moved ++ top = 0; ++ } + forwarder.setCurrentTouchEventOffsets(0, top); + } + }); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java +@@ -22,6 +22,7 @@ import org.chromium.chrome.browser.theme.TopUiThemeColorProvider; + import org.chromium.chrome.browser.toolbar.ControlContainer; + import org.chromium.chrome.features.start_surface.StartSurface; + import org.chromium.ui.resources.dynamics.DynamicResourceLoader; ++import org.chromium.chrome.browser.fullscreen.BrowserControlsManager; + + /** + * {@link LayoutManagerChromeTablet} is the specialization of {@link LayoutManagerChrome} for +@@ -54,7 +55,8 @@ public class LayoutManagerChromeTablet extends LayoutManagerChrome { + jankTracker); + + mTabStripLayoutHelperManager = new StripLayoutHelperManager( +- host.getContext(), this, mHost.getLayoutRenderHost(), () -> mLayerTitleCache); ++ host.getContext(), this, mHost.getLayoutRenderHost(), () -> mLayerTitleCache, ++ /*browserControlsManagerSupplier*/ () -> getBrowserControlsManager()); + addSceneOverlay(mTabStripLayoutHelperManager); + + addObserver(mTabStripLayoutHelperManager.getTabSwitcherObserver()); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java +@@ -34,6 +34,9 @@ import org.chromium.ui.base.DeviceFormFactor; + import org.chromium.ui.base.LocalizationUtils; + import org.chromium.ui.resources.ResourceManager; + ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + import java.util.ArrayList; + import java.util.List; + +@@ -381,7 +384,11 @@ public class ToolbarSwipeLayout extends Layout { + mLeftToolbarOverlay.setXOffset(leftX * dpToPx); + } + mLeftTab.setX(leftX); +- mLeftTab.setY(mBrowserControlsStateProvider.getContentOffset() / dpToPx); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ mLeftTab.setY(0); ++ } else { ++ mLeftTab.setY(mBrowserControlsStateProvider.getContentOffset() / dpToPx); ++ } + needUpdate = updateSnap(dt, mLeftTab) || needUpdate; + } else if (mLeftToolbarOverlay != null) { + mLeftToolbarOverlay.setManualVisibility(false); +@@ -394,7 +401,11 @@ public class ToolbarSwipeLayout extends Layout { + mRightToolbarOverlay.setXOffset(rightX * dpToPx); + } + mRightTab.setX(rightX); +- mRightTab.setY(mBrowserControlsStateProvider.getContentOffset() / dpToPx); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ mRightTab.setY(0); ++ } else { ++ mRightTab.setY(mBrowserControlsStateProvider.getContentOffset() / dpToPx); ++ } + needUpdate = updateSnap(dt, mRightTab) || needUpdate; + } else if (mRightToolbarOverlay != null) { + mRightToolbarOverlay.setManualVisibility(false); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java +@@ -316,7 +316,7 @@ public class StripLayoutHelper implements StripLayoutTab.StripLayoutTabDelegate + // position 0 is on the left. Account for that in the offset calculation. + boolean isRtl = LocalizationUtils.isLayoutRtl(); + boolean useUnadjustedScrollOffset = isRtl != isLeft; +- float offset = -(useUnadjustedScrollOffset ? mScrollOffset ++ float offset = -Math.abs(useUnadjustedScrollOffset ? mScrollOffset + : (mMinScrollOffset - mScrollOffset)); + + if (offset == 0.f) { +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java +@@ -49,6 +49,9 @@ import org.chromium.components.browser_ui.widget.animation.Interpolators; + import org.chromium.ui.base.LocalizationUtils; + import org.chromium.ui.resources.ResourceManager; + import org.chromium.url.GURL; ++import org.chromium.chrome.browser.fullscreen.BrowserControlsManager; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + import java.util.List; + +@@ -109,9 +112,13 @@ public class StripLayoutHelperManager implements SceneOverlay { + private final String mDefaultTitle; + private final Supplier mLayerTitleCacheSupplier; + ++ private final Supplier mBrowserControlsManagerSupplier; ++ private final float mDpToPx; ++ + private class TabStripEventHandler implements GestureHandler { + @Override + public void onDown(float x, float y, boolean fromMouse, int buttons) { ++ y -= mStripFilterArea.top; + if (mModelSelectorButton.onDown(x, y)) return; + if (mStripScrim.isVisible()) return; + getActiveStripLayoutHelper().onDown(time(), x, y, fromMouse, buttons); +@@ -131,6 +138,7 @@ public class StripLayoutHelperManager implements SceneOverlay { + + @Override + public void drag(float x, float y, float dx, float dy, float tx, float ty) { ++ y -= mStripFilterArea.top; + mModelSelectorButton.drag(x, y); + if (mStripScrim.isVisible()) return; + getActiveStripLayoutHelper().drag(time(), x, y, dx, dy, tx, ty); +@@ -138,6 +146,7 @@ public class StripLayoutHelperManager implements SceneOverlay { + + @Override + public void click(float x, float y, boolean fromMouse, int buttons) { ++ y -= mStripFilterArea.top; + long time = time(); + if (mModelSelectorButton.click(x, y)) { + mModelSelectorButton.handleClick(time); +@@ -149,13 +158,13 @@ public class StripLayoutHelperManager implements SceneOverlay { + + @Override + public void fling(float x, float y, float velocityX, float velocityY) { +- if (mStripScrim.isVisible()) return; ++ y -= mStripFilterArea.top; + getActiveStripLayoutHelper().fling(time(), x, y, velocityX, velocityY); + } + + @Override + public void onLongPress(float x, float y) { +- if (mStripScrim.isVisible()) return; ++ y -= mStripFilterArea.top; + getActiveStripLayoutHelper().onLongPress(time(), x, y); + } + +@@ -224,7 +233,8 @@ public class StripLayoutHelperManager implements SceneOverlay { + * @param layerTitleCacheSupplier A supplier of the cache that holds the title textures. + */ + public StripLayoutHelperManager(Context context, LayoutUpdateHost updateHost, +- LayoutRenderHost renderHost, Supplier layerTitleCacheSupplier) { ++ LayoutRenderHost renderHost, Supplier layerTitleCacheSupplier, ++ Supplier browserControlsManagerSupplier) { + mUpdateHost = updateHost; + mLayerTitleCacheSupplier = layerTitleCacheSupplier; + mTabStripTreeProvider = new TabStripSceneLayer(context); +@@ -236,6 +246,8 @@ public class StripLayoutHelperManager implements SceneOverlay { + + mNormalHelper = new StripLayoutHelper(context, updateHost, renderHost, false); + mIncognitoHelper = new StripLayoutHelper(context, updateHost, renderHost, true); ++ mBrowserControlsManagerSupplier = browserControlsManagerSupplier; ++ mDpToPx = context.getResources().getDisplayMetrics().density; + + CompositorOnClickHandler selectorClickHandler = new CompositorOnClickHandler() { + @Override +@@ -315,9 +327,13 @@ public class StripLayoutHelperManager implements SceneOverlay { + Tab selectedTab = mTabModelSelector.getCurrentModel().getTabAt( + mTabModelSelector.getCurrentModel().index()); + int selectedTabId = selectedTab == null ? TabModel.INVALID_TAB_INDEX : selectedTab.getId(); ++ int topControlsHeight = 0; ++ if (mBrowserControlsManagerSupplier.get() != null) { ++ topControlsHeight = mBrowserControlsManagerSupplier.get().getTopControlsHeight(); ++ } + mTabStripTreeProvider.pushAndUpdateStrip(this, mLayerTitleCacheSupplier.get(), + resourceManager, getActiveStripLayoutHelper().getStripLayoutTabsToRender(), yOffset, +- selectedTabId); ++ selectedTabId, viewport.height(), topControlsHeight); + return mTabStripTreeProvider; + } + +@@ -350,7 +366,16 @@ public class StripLayoutHelperManager implements SceneOverlay { + mNormalHelper.onSizeChanged(mWidth, mHeight); + mIncognitoHelper.onSizeChanged(mWidth, mHeight); + +- mStripFilterArea.set(0, 0, mWidth, Math.min(getHeight(), visibleViewportOffsetY)); ++ float top = 0; ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM) && ++ mBrowserControlsManagerSupplier.get() != null) { ++ // move the rectangle to grab the touch events as the tab list (in tablet mode) ++ // is down and is following the toolbar offset as it moves. ++ // values are in pixels. ++ top = height - ((mBrowserControlsManagerSupplier.get().getTopControlsHeight() ++ - mBrowserControlsManagerSupplier.get().getTopControlOffset()) / mDpToPx); ++ } ++ mStripFilterArea.set(0, top, mWidth, top + Math.min(getHeight(), visibleViewportOffsetY)); + mEventFilter.setEventArea(mStripFilterArea); + } + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/StaticTabSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/StaticTabSceneLayer.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/StaticTabSceneLayer.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/StaticTabSceneLayer.java +@@ -12,6 +12,9 @@ import org.chromium.chrome.browser.layouts.scene_layer.SceneLayer; + import org.chromium.ui.modelutil.PropertyKey; + import org.chromium.ui.modelutil.PropertyModel; + ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** + * A SceneLayer to render a static tab. + */ +@@ -45,7 +48,10 @@ public class StaticTabSceneLayer extends SceneLayer { + float x = model.get(LayoutTab.RENDER_X) * LayoutTab.sDpToPx; + float y = model.get(LayoutTab.CONTENT_OFFSET) + + model.get(LayoutTab.RENDER_Y) * LayoutTab.sDpToPx; +- ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // the page content window never moves, it is fixed at the top ++ y = 0; ++ } + StaticTabSceneLayerJni.get().updateTabLayer(mNativePtr, StaticTabSceneLayer.this, + model.get(LayoutTab.TAB_ID), model.get(LayoutTab.CAN_USE_LIVE_TEXTURE), + model.get(LayoutTab.BACKGROUND_COLOR), x, y, +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java +@@ -23,6 +23,9 @@ import org.chromium.components.browser_ui.styles.SemanticColorUtils; + import org.chromium.ui.resources.ResourceManager; + import org.chromium.ui.util.ColorUtils; + ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** + * A SceneLayer to render a tab stack. + * TODO(changwan): change layouts to share one instance of this. +@@ -83,6 +86,12 @@ public class TabListSceneLayer extends SceneLayer { + + TabListSceneLayerJni.get().beginBuildingFrame(mNativePtr, TabListSceneLayer.this); + ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // the tabs list content window is fixed at the top, where the top toolbar used to be ++ viewport.top = 0; ++ backgroundTopOffset = 0; ++ } ++ + // TODO(crbug.com/1070281): Use Supplier to get viewport and forward it to native, then + // updateLayer can become obsolete. + TabListSceneLayerJni.get().updateLayer(mNativePtr, TabListSceneLayer.this, tabListBgColor, +@@ -117,6 +126,11 @@ public class TabListSceneLayer extends SceneLayer { + contentOffset = browserControls.getContentOffset(); + } + ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ toolbarYOffset = 0; ++ contentOffset = 0; ++ } ++ + // TODO(dtrainor, clholgat): remove "* dpToPx" once the native part fully supports dp. + TabListSceneLayerJni.get().putTabLayer(mNativePtr, TabListSceneLayer.this, t.getId(), + relatedTabIds, mUseAdditionalIds, R.id.control_container, +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java +@@ -20,6 +20,8 @@ import org.chromium.chrome.browser.layouts.scene_layer.SceneLayer; + import org.chromium.chrome.browser.layouts.scene_layer.SceneOverlayLayer; + import org.chromium.ui.base.LocalizationUtils; + import org.chromium.ui.resources.ResourceManager; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + /** + * The Java component of what is basically a CC Layer that manages drawing the Tab Strip (which is +@@ -66,10 +68,19 @@ public class TabStripSceneLayer extends SceneOverlayLayer { + */ + public void pushAndUpdateStrip(StripLayoutHelperManager layoutHelper, + LayerTitleCache layerTitleCache, ResourceManager resourceManager, +- StripLayoutTab[] stripLayoutTabsToRender, float yOffset, int selectedTabId) { ++ StripLayoutTab[] stripLayoutTabsToRender, float yOffset, int selectedTabId, ++ float viewportHeight, int topControlsHeight) { + if (mNativePtr == 0) return; + +- final boolean visible = yOffset > -layoutHelper.getHeight(); ++ boolean visible = yOffset > -layoutHelper.getHeight(); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // the list of open tabs (in tablet mode) is moved down, above the top ++ // toolbar which is also below. ++ // values are in pixel. ++ yOffset = (((int)viewportHeight - topControlsHeight) / mDpToPx) - yOffset; ++ // and it disappears along with the moving toolbar with a higher range ++ visible = yOffset > (-layoutHelper.getHeight() - topControlsHeight); ++ } + // This will hide the tab strips if necessary. + TabStripSceneLayerJni.get().beginBuildingFrame( + mNativePtr, TabStripSceneLayer.this, visible); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java +@@ -14,6 +14,9 @@ import android.util.AttributeSet; + import android.view.View; + import android.widget.FrameLayout; + ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + import org.chromium.chrome.R; + import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener; + import org.chromium.components.browser_ui.widget.animation.Interpolators; +@@ -166,9 +169,11 @@ public class FindToolbarTablet extends FindToolbar { + + if (show && getVisibility() != View.VISIBLE && mCurrentAnimation != mAnimationEnter) { + View anchorView = getRootView().findViewById(R.id.toolbar); +- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); +- lp.topMargin = anchorView.getBottom() - mYInsetPx; +- setLayoutParams(lp); ++ if (!CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); ++ lp.topMargin = anchorView.getBottom() - mYInsetPx; ++ setLayoutParams(lp); ++ } + nextAnimator = mAnimationEnter; + } else if (!show && getVisibility() != View.GONE && mCurrentAnimation != mAnimationLeave) { + nextAnimator = mAnimationLeave; +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java +@@ -49,6 +49,9 @@ import org.chromium.ui.vr.VrModeObserver; + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** + * A class that manages browser control visibility and positioning. + */ +@@ -367,6 +370,14 @@ public class BrowserControlsManager + return mTopControlContainerHeight; + } + ++ @Override ++ public int getTopControlsHeightRealOffset() { ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) ++ return 0; ++ else ++ return mTopControlContainerHeight; ++ } ++ + @Override + public int getTopControlsMinHeight() { + return mTopControlsMinHeight; +@@ -433,6 +444,8 @@ public class BrowserControlsManager + + @Override + public float getTopVisibleContentOffset() { ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) ++ return 0; + return getTopControlsHeight() + getTopControlOffset(); + } + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java +@@ -16,6 +16,10 @@ import org.chromium.chrome.browser.flags.ChromeFeatureList; + import org.chromium.chrome.browser.fullscreen.BrowserControlsManager; + import org.chromium.components.messages.MessageContainer; + ++import android.view.Gravity; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** + * Coordinator of {@link MessageContainer}, which can adjust margins of the message container + * and control the visibility of browser control when message is being shown. +@@ -45,7 +49,12 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider + } + CoordinatorLayout.LayoutParams params = + (CoordinatorLayout.LayoutParams) mContainer.getLayoutParams(); +- params.topMargin = getContainerTopOffset(); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ params.gravity = Gravity.START | Gravity.BOTTOM; ++ params.bottomMargin = getContainerTopOffset(); ++ } else { ++ params.topMargin = getContainerTopOffset(); ++ } + mContainer.setLayoutParams(params); + } + +@@ -87,6 +96,12 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider + + /** @return Offset of the message container from the top of the screen. */ + private int getContainerTopOffset() { ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ return mControlsManager.getContentOffset() ++ + (mControlsManager.getBottomControlsHeight() - mControlsManager.getBottomControlOffset()) ++ + mContainer.getMessageShadowTopMargin(); ++ } ++ + if (mControlsManager.getContentOffset() == 0) return 0; + final Resources res = mContainer.getResources(); + return mControlsManager.getContentOffset() +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenter.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenter.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenter.java +@@ -275,7 +275,7 @@ public class ChromeTabModalPresenter + Resources resources, BrowserControlsStateProvider provider) { + int scrimVerticalMargin = + resources.getDimensionPixelSize(R.dimen.tab_modal_scrim_vertical_margin); +- return provider.getTopControlsHeight() - scrimVerticalMargin; ++ return provider.getTopControlsHeightRealOffset() - scrimVerticalMargin; + } + + /** +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java +@@ -95,6 +95,8 @@ import org.chromium.content_public.browser.NavigationEntry; + import org.chromium.ui.base.DeviceFormFactor; + import org.chromium.ui.base.WindowAndroid; + import org.chromium.ui.mojom.WindowOpenDisposition; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + import java.util.List; + +@@ -547,10 +549,15 @@ public class NewTabPage implements NativePage, InvalidationAwareThumbnailProvide + // + topControlsDistanceToRest| will give the margin for the current animation frame. + final int topControlsDistanceToRest = mBrowserControlsStateProvider.getContentOffset() + - mBrowserControlsStateProvider.getTopControlsHeight(); +- final int topMargin = getToolbarExtraYOffset() + topControlsDistanceToRest; ++ int topMargin = getToolbarExtraYOffset() + topControlsDistanceToRest; + +- final int bottomMargin = mBrowserControlsStateProvider.getBottomControlsHeight() ++ int bottomMargin = mBrowserControlsStateProvider.getBottomControlsHeight() + - mBrowserControlsStateProvider.getBottomControlOffset(); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // move the margin of the new tab page up if the top toolbar is at the bottom ++ bottomMargin += mBrowserControlsStateProvider.getTopControlsHeight(); ++ topMargin = 0; ++ } + + if (topMargin != layoutParams.topMargin || bottomMargin != layoutParams.bottomMargin) { + layoutParams.topMargin = topMargin; +@@ -573,7 +580,7 @@ public class NewTabPage implements NativePage, InvalidationAwareThumbnailProvide + * strip. + */ + private int getToolbarExtraYOffset() { +- return mBrowserControlsStateProvider.getTopControlsHeight() - mTabStripAndToolbarHeight; ++ return mBrowserControlsStateProvider.getTopControlsHeightRealOffset(); + } + + /** @return The view container for the new tab layout. */ +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java +@@ -23,6 +23,8 @@ import org.chromium.chrome.browser.ui.native_page.NativePageHost; + import org.chromium.components.embedder_support.util.UrlConstants; + import org.chromium.ui.base.DeviceFormFactor; + import org.chromium.ui.base.ViewUtils; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + /** + * The native recent tabs page. Lists recently closed tabs, open windows and tabs from the user's +@@ -49,6 +51,7 @@ public class RecentTabsPage + private int mSnapshotListTop; + private int mSnapshotWidth; + private int mSnapshotHeight; ++ private final int mTabStripAndToolbarHeight; + + /** + * Whether {@link #mView} is attached to the application window. +@@ -68,6 +71,8 @@ public class RecentTabsPage + mActivity = activity; + mRecentTabsManager = recentTabsManager; + mPageHost = pageHost; ++ mTabStripAndToolbarHeight = ++ activity.getResources().getDimensionPixelSize(R.dimen.tab_strip_and_toolbar_height); + Resources resources = activity.getResources(); + + mTitle = resources.getString(R.string.recent_tabs); +@@ -85,7 +90,8 @@ public class RecentTabsPage + + mView.addOnAttachStateChangeListener(this); + +- if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity)) { ++ if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity) || ++ CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { + mBrowserControlsStateProvider = browserControlsStateProvider; + mBrowserControlsStateProvider.addObserver(this); + onBottomControlsHeightChanged(mBrowserControlsStateProvider.getBottomControlsHeight(), +@@ -267,7 +273,7 @@ public class RecentTabsPage + + private void updateMargins() { + final View recentTabsRoot = mView.findViewById(R.id.recent_tabs_root); +- final int topControlsHeight = mBrowserControlsStateProvider.getTopControlsHeight(); ++ final int topControlsHeight = mBrowserControlsStateProvider.getTopControlsHeightRealOffset(); + final int contentOffset = mBrowserControlsStateProvider.getContentOffset(); + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) recentTabsRoot.getLayoutParams(); +@@ -283,9 +289,17 @@ public class RecentTabsPage + + // If the content offset is different from the margin, we use translationY to position the + // view in line with the content offset. +- recentTabsRoot.setTranslationY(contentOffset - topMargin); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ topMargin = 0; ++ recentTabsRoot.setTranslationY(0); ++ } else { ++ recentTabsRoot.setTranslationY(contentOffset - topMargin); ++ } + +- final int bottomMargin = mBrowserControlsStateProvider.getBottomControlsHeight(); ++ int bottomMargin = mBrowserControlsStateProvider.getBottomControlsHeight(); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ bottomMargin += mBrowserControlsStateProvider.getTopControlsHeight(); ++ } + if (topMargin != layoutParams.topMargin || bottomMargin != layoutParams.bottomMargin) { + layoutParams.topMargin = topMargin; + layoutParams.bottomMargin = bottomMargin; +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java +@@ -67,6 +67,11 @@ import org.chromium.ui.base.WindowDelegate; + import org.chromium.ui.modaldialog.ModalDialogManager; + import org.chromium.url.GURL; + ++import android.view.Gravity; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++import androidx.coordinatorlayout.widget.CoordinatorLayout; ++ + import java.lang.ref.WeakReference; + + /** Queries the user's default search engine and shows autocomplete suggestions. */ +@@ -178,6 +183,11 @@ public class SearchActivity extends AsyncInitializationActivity + mSearchBox = (SearchActivityLocationBarLayout) mContentView.findViewById( + R.id.search_location_bar); + View anchorView = mContentView.findViewById(R.id.toolbar); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) ++ anchorView.getLayoutParams(); ++ layoutParams.gravity = Gravity.START | Gravity.BOTTOM; ++ } + OverrideUrlLoadingDelegate overrideUrlLoadingDelegate = + (String url, @PageTransition int transition, String postDataType, byte[] postData, + boolean incognito) -> { +@@ -185,7 +195,7 @@ public class SearchActivity extends AsyncInitializationActivity + return true; + }; + // clang-format off +- mLocationBarCoordinator = new LocationBarCoordinator(mSearchBox, anchorView, ++ mLocationBarCoordinator = new LocationBarCoordinator(mSearchBox, anchorView, anchorView, + mProfileSupplier, PrivacyPreferencesManagerImpl.getInstance(), + mSearchBoxDataProvider, null, new WindowDelegate(getWindow()), getWindowAndroid(), + /*activityTabSupplier=*/() -> null, getModalDialogManagerSupplier(), +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java +@@ -252,6 +252,11 @@ public class SettingsActivity extends ChromeBaseAppCompatActivity + if (fragment instanceof INeedSnackbarManager) { + ((INeedSnackbarManager)fragment).setSnackbarManager(mSnackbarManager); + } ++ if (fragment instanceof AccessibilitySettings) { ++ ((ChromeAccessibilitySettingsDelegate) ++ ((AccessibilitySettings) fragment) ++ .getDelegate()).setSnackbarManager(mSnackbarManager); ++ } + } + + @Override +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java +@@ -22,6 +22,11 @@ import org.chromium.ui.modelutil.PropertyModelChangeProcessor; + import org.chromium.ui.resources.ResourceManager; + import org.chromium.ui.resources.dynamics.ViewResourceAdapter; + ++import android.view.Gravity; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++import androidx.coordinatorlayout.widget.CoordinatorLayout; ++ + /** + * The coordinator for a status indicator that is positioned below the status bar and is persistent. + * Typically used to relay status, e.g. indicate user is offline. +@@ -168,6 +173,11 @@ public class StatusIndicatorCoordinator { + private void initialize() { + final ViewStub stub = mActivity.findViewById(R.id.status_indicator_stub); + final ViewResourceFrameLayout root = (ViewResourceFrameLayout) stub.inflate(); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // status messages (such as the offline indicator) are docked at the bottom ++ CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams)root.getLayoutParams(); ++ layoutParams.gravity = Gravity.START | Gravity.BOTTOM; ++ } + mResourceId = root.getId(); + mSceneLayer.setResourceId(mResourceId); + mResourceAdapter = root.getResourceAdapter(); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java +@@ -15,6 +15,8 @@ import org.chromium.chrome.browser.layouts.components.VirtualView; + import org.chromium.chrome.browser.layouts.scene_layer.SceneLayer; + import org.chromium.chrome.browser.layouts.scene_layer.SceneOverlayLayer; + import org.chromium.ui.resources.ResourceManager; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + import java.util.List; + +@@ -77,7 +79,10 @@ class StatusIndicatorSceneLayer extends SceneOverlayLayer implements SceneOverla + @Override + public SceneOverlayLayer getUpdatedSceneOverlayTree( + RectF viewport, RectF visibleViewport, ResourceManager resourceManager, float yOffset) { +- final int offset = mBrowserControlsStateProvider.getTopControlsMinHeightOffset(); ++ int offset = mBrowserControlsStateProvider.getTopControlsMinHeightOffset(); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ offset = (int)viewport.bottom - offset; ++ } + StatusIndicatorSceneLayerJni.get().updateStatusIndicatorLayer( + mNativePtr, StatusIndicatorSceneLayer.this, resourceManager, mResourceId, offset); + return this; +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java +@@ -172,6 +172,9 @@ import org.chromium.url.GURL; + + import java.util.List; + ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** + * Contains logic for managing the toolbar visual component. This class manages the interactions + * with the rest of the application to ensure the toolbar is always visually up to date. +@@ -605,7 +608,7 @@ public class ToolbarManager implements UrlFocusChangeListener, ThemeColorObserve + }; + // clang-format off + LocationBarCoordinator locationBarCoordinator = new LocationBarCoordinator( +- mActivity.findViewById(R.id.location_bar), toolbarLayout, profileSupplier, ++ mActivity.findViewById(R.id.location_bar), toolbarLayout, controlContainer, profileSupplier, + PrivacyPreferencesManagerImpl.getInstance(), mLocationBarModel, + mActionModeController.getActionModeCallback(), + new WindowDelegate(mActivity.getWindow()), windowAndroid, mActivityTabProvider, +@@ -849,11 +852,13 @@ public class ToolbarManager implements UrlFocusChangeListener, ThemeColorObserve + // the height won't be measured by the background image. + if (mControlContainer.getBackground() == null) { + setControlContainerTopMargin(getToolbarExtraYOffset()); ++ MoveBottomBarOverTopBar(); + } else if (mLayoutChangeListener == null) { + mLayoutChangeListener = (view, left, top, right, bottom, oldLeft, oldTop, + oldRight, oldBottom) -> { + if (mControlContainer.getBackground() == null) { + setControlContainerTopMargin(getToolbarExtraYOffset()); ++ MoveBottomBarOverTopBar(); + mControlContainer.removeOnLayoutChangeListener(mLayoutChangeListener); + mLayoutChangeListener = null; + } +@@ -1235,13 +1240,25 @@ public class ToolbarManager implements UrlFocusChangeListener, ThemeColorObserve + return mLocationBar.getOmniboxStub().isUrlBarFocused(); + } + ++ View bottomRoot; ++ ++ private void MoveBottomBarOverTopBar() { ++ if (bottomRoot != null && ++ CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // move up the container view of the ui ++ // below there is the toolbar ++ bottomRoot.setTranslationY(-mBrowserControlsSizer.getTopControlsHeight()); ++ } ++ } ++ + /** + * Enable the bottom controls. + */ + public void enableBottomControls() { +- View root = ((ViewStub) mActivity.findViewById(R.id.bottom_controls_stub)).inflate(); ++ bottomRoot = ((ViewStub) mActivity.findViewById(R.id.bottom_controls_stub)).inflate(); ++ MoveBottomBarOverTopBar(); + mTabGroupUi = TabManagementModuleProvider.getDelegate().createTabGroupUi(mActivity, +- root.findViewById(R.id.bottom_container_slot), mIncognitoStateProvider, ++ bottomRoot.findViewById(R.id.bottom_container_slot), mIncognitoStateProvider, + mScrimCoordinator, mOmniboxFocusStateSupplier, mBottomSheetController, + mActivityLifecycleDispatcher, mIsWarmOnResumeSupplier, mTabModelSelector, + mTabContentManager, mCompositorViewHolder, +@@ -1250,8 +1267,9 @@ public class ToolbarManager implements UrlFocusChangeListener, ThemeColorObserve + mBottomControlsCoordinatorSupplier.set( + new BottomControlsCoordinator(mActivity, mWindowAndroid, mLayoutManager, + mCompositorViewHolder.getResourceManager(), mBrowserControlsSizer, +- mFullscreenManager, (ScrollingBottomViewResourceFrameLayout) root, +- mTabGroupUi, mOverlayPanelVisibilitySupplier)); ++ mFullscreenManager, (ScrollingBottomViewResourceFrameLayout) bottomRoot, ++ mTabGroupUi, mOverlayPanelVisibilitySupplier, ++ mTopUiThemeColorProvider, mActivityTabProvider)); + } + + /** +@@ -1982,6 +2000,15 @@ public class ToolbarManager implements UrlFocusChangeListener, ThemeColorObserve + private void setControlContainerTopMargin(int margin) { + final ViewGroup.MarginLayoutParams layoutParams = + ((ViewGroup.MarginLayoutParams) mControlContainer.getLayoutParams()); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ if (layoutParams.bottomMargin == margin) { ++ return; ++ } ++ ++ layoutParams.bottomMargin = margin; ++ mControlContainer.setLayoutParams(layoutParams); ++ return; ++ } + if (layoutParams.topMargin == margin) { + return; + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java +@@ -15,6 +15,8 @@ import org.chromium.base.lifetime.Destroyable; + import org.chromium.base.supplier.ObservableSupplier; + import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; + import org.chromium.ui.base.ApplicationViewportInsetSupplier; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + /** + * The container that holds both infobars and snackbars. It will be translated up and down when the +@@ -62,10 +64,27 @@ public class BottomContainer + setTranslationY(mBaseYOffset); + } + ++ @Override ++ public void onTopControlsHeightChanged(int topControlsHeight, int topControlsMinHeight) { ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) ++ setTranslationY(mBaseYOffset); ++ } ++ ++ @Override ++ public void onAndroidVisibilityChanged(int visibility) { ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) ++ setTranslationY(mBaseYOffset); ++ } ++ + @Override + public void setTranslationY(float y) { + mBaseYOffset = y; + ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // the snackbar container is moved up because there is the top toolbar at the bottom ++ mBaseYOffset = -(mBrowserControlsStateProvider.getTopControlsHeight() ++ + mBrowserControlsStateProvider.getTopControlOffset()); ++ } + float offsetFromControls = mBrowserControlsStateProvider.getBottomControlOffset() + - mBrowserControlsStateProvider.getBottomControlsHeight(); + offsetFromControls -= mViewportInsetSupplier.get(); +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -6411,6 +6411,11 @@ const FeatureEntry kFeatureEntries[] = { + flag_descriptions::kImpulseScrollAnimationsDescription, kOsAll, + FEATURE_VALUE_TYPE(features::kImpulseScrollAnimations)}, + ++ {"move-top-toolbar-to-bottom", ++ flag_descriptions::kMoveTopToolbarToBottomName, ++ flag_descriptions::kMoveTopToolbarToBottomDescription, kOsAndroid, ++ FEATURE_VALUE_TYPE(features::kMoveTopToolbarToBottom)}, ++ + {"percent-based-scrolling", flag_descriptions::kPercentBasedScrollingName, + flag_descriptions::kPercentBasedScrollingDescription, kOsAll, + FEATURE_VALUE_TYPE(features::kPercentBasedScrolling)}, +diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc +--- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc ++++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc +@@ -12,6 +12,7 @@ + #include "ui/android/resources/nine_patch_resource.h" + #include "ui/android/resources/resource_manager_impl.h" + #include "ui/gfx/geometry/transform.h" ++#include "cc/base/features.h" + + using base::android::JavaParamRef; + using base::android::JavaRef; +@@ -74,8 +75,10 @@ void TabStripSceneLayer::SetContentTree( + content_tree_ = content_tree; + if (content_tree) { + layer()->InsertChild(content_tree->layer(), 0); +- content_tree->layer()->SetPosition( +- gfx::PointF(0, -layer()->position().y())); ++ if (!base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) { ++ content_tree->layer()->SetPosition( ++ gfx::PointF(0, -layer()->position().y())); ++ } + } + } + } +@@ -110,7 +113,12 @@ void TabStripSceneLayer::UpdateTabStripLayer(JNIEnv* env, + jboolean should_readd_background) { + background_tab_brightness_ = background_tab_brightness; + gfx::RectF content(0, y_offset, width, height); +- layer()->SetPosition(gfx::PointF(0, y_offset)); ++ if (base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) { ++ // do not move the whole layer (which also contains the contents) but only the tab strip layer ++ tab_strip_layer_->SetPosition(gfx::PointF(0, y_offset)); ++ } else { ++ layer()->SetPosition(gfx::PointF(0, y_offset)); ++ } + tab_strip_layer_->SetBounds(gfx::Size(width, height)); + scrollable_strip_layer_->SetBounds(gfx::Size(width, height)); + +@@ -123,7 +131,7 @@ void TabStripSceneLayer::UpdateTabStripLayer(JNIEnv* env, + } + + // Content tree should not be affected by tab strip scene layer visibility. +- if (content_tree_) ++ if (content_tree_ && !base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) + content_tree_->layer()->SetPosition(gfx::PointF(0, -y_offset)); + + // Make sure tab strip changes are committed after rotating the device. +diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsMarginSupplier.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsMarginSupplier.java +--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsMarginSupplier.java ++++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsMarginSupplier.java +@@ -8,6 +8,8 @@ import android.graphics.Rect; + + import org.chromium.base.supplier.DestroyableObservableSupplier; + import org.chromium.base.supplier.ObservableSupplierImpl; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + /** + * An implementation of {@link DestroyableObservableSupplier} that monitors changes to browser +@@ -52,6 +54,10 @@ public class BrowserControlsMarginSupplier extends ObservableSupplierImpl + + mBrowserControlsStateProvider.getTopControlOffset(); + int bottomMargin = mBrowserControlsStateProvider.getBottomControlsHeight() + - mBrowserControlsStateProvider.getBottomControlOffset(); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ bottomMargin += topMargin; ++ topMargin = 0; ++ } + super.set(new Rect(0, topMargin, 0, bottomMargin)); + } + } +diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java +--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java ++++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java +@@ -64,6 +64,12 @@ public interface BrowserControlsStateProvider { + */ + int getTopControlsHeight(); + ++ /** ++ * @return The height of the top controls in pixels. ++ * returns 0 is the toolbar is at the bottom ++ */ ++ int getTopControlsHeightRealOffset(); ++ + /** + * @return The minimum visible height top controls can have in pixels. + */ +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -1569,6 +1569,10 @@ const char kImpulseScrollAnimationsDescription[] = + "Replaces the default scroll animation with Impulse-style scroll " + "animations."; + ++const char kMoveTopToolbarToBottomName[] = "Move top toolbar to bottom"; ++const char kMoveTopToolbarToBottomDescription[] = ++ "Move the top toolbar to the bottom."; ++ + const char kIncognitoBrandConsistencyForAndroidName[] = + "Enable Incognito brand consistency in Android."; + const char kIncognitoBrandConsistencyForAndroidDescription[] = +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -901,6 +901,9 @@ extern const char kCompositorThreadedScrollbarScrollingDescription[]; + extern const char kImpulseScrollAnimationsName[]; + extern const char kImpulseScrollAnimationsDescription[]; + ++extern const char kMoveTopToolbarToBottomName[]; ++extern const char kMoveTopToolbarToBottomDescription[]; ++ + extern const char kIncognitoBrandConsistencyForAndroidName[]; + extern const char kIncognitoBrandConsistencyForAndroidDescription[]; + +diff --git a/chrome/browser/flags/android/cached_feature_flags.cc b/chrome/browser/flags/android/cached_feature_flags.cc +--- a/chrome/browser/flags/android/cached_feature_flags.cc ++++ b/chrome/browser/flags/android/cached_feature_flags.cc +@@ -13,6 +13,7 @@ + #include "components/prefs/pref_service.h" + #include "content/public/common/content_features.h" + #include "content/public/common/network_service_util.h" ++#include "components/flags_ui/pref_service_flags_storage.h" + + using base::android::ConvertJavaStringToUTF8; + using base::android::ConvertUTF8ToJavaString; +@@ -52,3 +53,20 @@ static ScopedJavaLocalRef JNI_CachedFeatureFlags_GetAdBlockFiltersURL(J + static void JNI_CachedFeatureFlags_SetAdBlockFiltersURL(JNIEnv* env, const JavaParamRef& url) { + g_browser_process->local_state()->SetString(prefs::kAdBlockFiltersURL, base::android::ConvertJavaStringToUTF8(env, url)); + } ++ ++static void JNI_CachedFeatureFlags_SetEnabled(JNIEnv* env, const JavaParamRef& featureName, jboolean isEnabled) { ++ flags_ui::PrefServiceFlagsStorage flags_storage( ++ g_browser_process->local_state()); ++ std::set entries = flags_storage.GetFlags(); ++ ++ auto internal_name = base::android::ConvertJavaStringToUTF8(env, featureName); ++ entries.erase(internal_name); ++ entries.erase(internal_name + "@1"); // enabled ++ entries.erase(internal_name + "@2"); // disabled ++ ++ if (isEnabled) ++ entries.insert(internal_name + "@1"); ++ ++ flags_storage.SetFlags(entries); ++ flags_storage.CommitPendingWrites(); ++} +diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc +--- a/chrome/browser/flags/android/chrome_feature_list.cc ++++ b/chrome/browser/flags/android/chrome_feature_list.cc +@@ -11,6 +11,7 @@ + #include "base/android/jni_array.h" + #include "base/android/jni_string.h" + #include "base/feature_list.h" ++#include "cc/base/features.h" + #include "base/metrics/field_trial_params.h" + #include "chrome/browser/browser_features.h" + #include "chrome/browser/feature_guide/notifications/feature_notification_guide_service.h" +@@ -249,6 +250,7 @@ const base::Feature* const kFeaturesExposedToJava[] = { + &kInstantStart, + &kKitKatSupported, + &kLensCameraAssistedSearch, ++ &features::kMoveTopToolbarToBottom, + &kNewWindowAppMenu, + &kOfflineIndicatorV2, + &kPageAnnotationsService, +diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java +--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java ++++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java +@@ -105,6 +105,7 @@ public class CachedFeatureFlags { + .put(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS, false) + .put(ChromeFeatureList.TAB_GROUPS_FOR_TABLETS, false) + .put(ChromeFeatureList.TAB_STRIP_IMPROVEMENTS, false) ++ .put(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM, false) + .build(); + + /** +@@ -203,6 +204,23 @@ public class CachedFeatureFlags { + SharedPreferencesManager.getInstance().writeBoolean(preferenceName, isEnabledInNative); + } + ++ /** ++ * Allows the modification of the flag value on the java side. ++ * Currently only the feature flag with on / off values is managed. ++ * ++ * @param featureName the feature name from ChromeFeatureList. ++ * @param flagName the flag name name from about_flags.cc. ++ */ ++ public static void setFlagEnabled(String featureName, String flagName, Boolean newValue) { ++ CachedFeatureFlagsJni.get().setEnabled(flagName, newValue); ++ ++ String preferenceName = getPrefForFeatureFlag(featureName); ++ SharedPreferencesManager.getInstance().writeBoolean(preferenceName, newValue); ++ synchronized (sValuesReturned.boolValues) { ++ sValuesReturned.boolValues.put(preferenceName, newValue); ++ } ++ } ++ + /** + * Forces a feature to be enabled or disabled for testing. + * +@@ -496,6 +514,7 @@ public class CachedFeatureFlags { + + @NativeMethods + interface Natives { ++ void setEnabled(String featureName, boolean newValue); + boolean isNetworkServiceWarmUpEnabled(); + void setAdBlockFiltersURL(String url); + String getAdBlockFiltersURL(); +diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java ++++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +@@ -403,6 +403,8 @@ public abstract class ChromeFeatureList { + public static final String MOBILE_IDENTITY_CONSISTENCY_PROMOS = + "MobileIdentityConsistencyPromos"; + public static final String MODAL_PERMISSION_DIALOG_VIEW = "ModalPermissionDialogView"; ++ public static final String MOVE_TOP_TOOLBAR_TO_BOTTOM = ++ "MoveTopToolbarToBottom"; + public static final String METRICS_SETTINGS_ANDROID = "MetricsSettingsAndroid"; + public static final String NEW_WINDOW_APP_MENU = "NewWindowAppMenu"; + public static final String SEARCH_READY_OMNIBOX = "SearchReadyOmnibox"; +diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java +--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java ++++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java +@@ -42,6 +42,8 @@ import org.chromium.base.SysUtils; + import org.chromium.base.metrics.RecordHistogram; + import org.chromium.base.task.PostTask; + import org.chromium.base.task.TaskTraits; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + import org.chromium.chrome.browser.ui.appmenu.internal.R; + import org.chromium.components.browser_ui.widget.chips.ChipView; + import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter; +@@ -515,6 +517,8 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuClickHandler + View anchorView, @IdRes int groupDividerResourceId) { + anchorView.getLocationOnScreen(mTempLocation); + int anchorViewY = mTempLocation[1] - appDimensions.top; ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) ++ anchorViewY += mNegativeVerticalOffsetNotTopAnchored; + + int anchorViewImpactHeight = mIsByPermanentButton ? anchorView.getHeight() : 0; + +diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java +--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java ++++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java +@@ -86,6 +86,7 @@ public final class LocationBarCoordinator implements LocationBar, NativeInitObse + private WindowDelegate mWindowDelegate; + private WindowAndroid mWindowAndroid; + private View mAutocompleteAnchorView; ++ private View mContainerView; + private LocationBarMediator mLocationBarMediator; + private View mUrlBar; + private View mDeleteButton; +@@ -140,7 +141,7 @@ public final class LocationBarCoordinator implements LocationBar, NativeInitObse + * MerchantTrustSignalsCoordinator}. Can be null if a store icon shouldn't be shown, + * such as when called from a search activity. + */ +- public LocationBarCoordinator(View locationBarLayout, View autocompleteAnchorView, ++ public LocationBarCoordinator(View locationBarLayout, View autocompleteAnchorView, View containerView, + ObservableSupplier profileObservableSupplier, + PrivacyPreferencesManager privacyPreferencesManager, + LocationBarDataProvider locationBarDataProvider, ActionMode.Callback actionModeCallback, +@@ -170,6 +171,7 @@ public final class LocationBarCoordinator implements LocationBar, NativeInitObse + mActivityLifecycleDispatcher = activityLifecycleDispatcher; + mActivityLifecycleDispatcher.register(this); + mAutocompleteAnchorView = autocompleteAnchorView; ++ mContainerView = containerView; + + mUrlBar = mLocationBarLayout.findViewById(R.id.url_bar); + // TODO(crbug.com/1151513): Inject LocaleManager instance to LocationBarCoordinator instead +@@ -369,6 +371,11 @@ public final class LocationBarCoordinator implements LocationBar, NativeInitObse + return mAutocompleteAnchorView; + } + ++ @Override ++ public View getAnchorContainerView() { ++ return mContainerView; ++ } ++ + @Override + public View getAlignmentView() { + return isTablet() ? mLocationBarLayout : null; +diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java +--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java ++++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java +@@ -27,6 +27,9 @@ import org.chromium.ui.modelutil.PropertyModelChangeProcessor; + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** + * Coordinates the interactions with the UrlBar text component. + */ +@@ -210,7 +213,13 @@ public class UrlBarCoordinator implements UrlBarEditingTextStateProvider, UrlFoc + // to show or hide keyboard anyway. This may happen when we schedule keyboard hide, and + // receive a second request to hide the keyboard instantly. + if (showKeyboard) { +- setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN, /* delay */ false); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // probably due to an android bug, fix the size rather than pan the view. ++ // with the pan the bar may not always follow the focus if not at the first input by the user ++ setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE, /* delay */ false); ++ } else { ++ setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN, /* delay */ false); ++ } + mKeyboardVisibilityDelegate.showKeyboard(mUrlBar); + } else { + // The animation rendering may not yet be 100% complete and hiding the keyboard makes +diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java +--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java ++++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java +@@ -59,6 +59,8 @@ import org.chromium.ui.modelutil.LazyConstructionPropertyMcp; + import org.chromium.ui.modelutil.MVCListAdapter; + import org.chromium.ui.modelutil.MVCListAdapter.ModelList; + import org.chromium.ui.modelutil.PropertyModel; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + import java.util.ArrayList; + import java.util.List; +@@ -72,6 +74,7 @@ public class AutocompleteCoordinator implements UrlFocusChangeListener, UrlTextC + private final @NonNull Callback mProfileChangeCallback; + private final @NonNull AutocompleteMediator mMediator; + private @Nullable OmniboxSuggestionsDropdown mDropdown; ++ private final @NonNull OmniboxSuggestionsDropdownEmbedder mDropdownEmbedder; + + public AutocompleteCoordinator(@NonNull ViewGroup parent, + @NonNull AutocompleteDelegate delegate, +@@ -93,6 +96,7 @@ public class AutocompleteCoordinator implements UrlFocusChangeListener, UrlTextC + PropertyModel listModel = new PropertyModel(SuggestionListProperties.ALL_KEYS); + ModelList listItems = new ModelList(); + ++ mDropdownEmbedder = dropdownEmbedder; + listModel.set(SuggestionListProperties.EMBEDDER, dropdownEmbedder); + listModel.set(SuggestionListProperties.VISIBLE, false); + listModel.set(SuggestionListProperties.SUGGESTION_MODELS, listItems); +@@ -142,7 +146,7 @@ public class AutocompleteCoordinator implements UrlFocusChangeListener, UrlTextC + public void inflate() { + OmniboxSuggestionsDropdown dropdown; + try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { +- dropdown = new OmniboxSuggestionsDropdown(context); ++ dropdown = new OmniboxSuggestionsDropdown(context, mDropdownEmbedder); + } + + // Start with visibility GONE to ensure that show() is called. +@@ -213,6 +217,16 @@ public class AutocompleteCoordinator implements UrlFocusChangeListener, UrlTextC + ViewGroup container = (ViewGroup) ((ViewStub) mParent.getRootView().findViewById( + R.id.omnibox_results_container_stub)) + .inflate(); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // make margins works ++ dropdown.getViewGroup().setClipToPadding(true); ++ container.bringToFront(); ++ ++ // do not cover the bar ++ ViewGroup.LayoutParams params = container.getLayoutParams(); ++ ((ViewGroup.MarginLayoutParams) params).bottomMargin = ++ mDropdownEmbedder.getAnchorView().getMeasuredHeight(); ++ } + + mHolder = new SuggestionListViewHolder(container, dropdown); + for (int i = 0; i < mCallbacks.size(); i++) { +diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java +--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java ++++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java +@@ -39,6 +39,9 @@ import org.chromium.ui.base.ViewUtils; + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** A widget for showing a list of omnibox suggestions. */ + public class OmniboxSuggestionsDropdown extends RecyclerView { + private static final long DEFERRED_INITIAL_SHRINKING_LAYOUT_FROM_IME_DURATION_MS = 300; +@@ -149,7 +152,8 @@ public class OmniboxSuggestionsDropdown extends RecyclerView { + * Constructs a new list designed for containing omnibox suggestions. + * @param context Context used for contained views. + */ +- public OmniboxSuggestionsDropdown(@NonNull Context context) { ++ public OmniboxSuggestionsDropdown(@NonNull Context context, ++ @NonNull OmniboxSuggestionsDropdownEmbedder embedder) { + super(context, null, android.R.attr.dropDownListViewStyle); + setFocusable(true); + setFocusableInTouchMode(true); +@@ -160,7 +164,7 @@ public class OmniboxSuggestionsDropdown extends RecyclerView { + + mScrollListener = new SuggestionScrollListener(); + setOnScrollListener(mScrollListener); +- setLayoutManager(new LinearLayoutManager(context) { ++ LinearLayoutManager linearLayoutManager = (new LinearLayoutManager(context) { + @Override + public int scrollVerticallyBy( + int deltaY, RecyclerView.Recycler recycler, RecyclerView.State state) { +@@ -175,6 +179,19 @@ public class OmniboxSuggestionsDropdown extends RecyclerView { + final Resources resources = context.getResources(); + int paddingBottom = + resources.getDimensionPixelOffset(R.dimen.omnibox_suggestion_list_padding_bottom); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // reverse the layout so that the items are at the bottom (in reverse order) ++ // and anchored to the bottom edge ++ linearLayoutManager.setReverseLayout(true); ++ ++ if (!embedder.isTablet()) { ++ ViewGroup.MarginLayoutParams embedderParams = (ViewGroup.MarginLayoutParams) ++ embedder.getAnchorContainerView().getLayoutParams(); ++ paddingBottom = resources.getDimensionPixelOffset(R.dimen.toolbar_height_no_shadow) + ++ embedderParams.bottomMargin; ++ } ++ } ++ setLayoutManager(linearLayoutManager); + ViewCompat.setPaddingRelative(this, 0, 0, 0, paddingBottom); + + mStandardBgColor = ChromeColors.getDefaultThemeColor(context, false); +@@ -347,6 +364,8 @@ public class OmniboxSuggestionsDropdown extends RecyclerView { + } + + private int calculateAnchorBottomRelativeToContent() { ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) ++ return 0; + View contentView = + mEmbedder.getAnchorView().getRootView().findViewById(android.R.id.content); + ViewUtils.getRelativeLayoutPosition(contentView, mAnchorView, mTempPosition); +diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java +--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java ++++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java +@@ -17,6 +17,10 @@ public interface OmniboxSuggestionsDropdownEmbedder { + @NonNull + View getAnchorView(); + ++ /** Return the container view the suggestion list should be drawn in. */ ++ @NonNull ++ View getAnchorContainerView(); ++ + /** + * Return the view that the omnibox suggestions should be aligned horizontally to. The + * view must be a descendant of {@link #getAnchorView()}. If null, the suggestions will +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -1394,6 +1394,12 @@ Your Google account may have other forms of browsing history like searches and a + + Force Tablet Mode + ++ ++ Move toolbar to bottom ++ ++ ++ Move toolbar to bottom ++ + + + +diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn +--- a/chrome/browser/ui/android/toolbar/BUILD.gn ++++ b/chrome/browser/ui/android/toolbar/BUILD.gn +@@ -150,6 +150,7 @@ android_library("java") { + "//third_party/android_deps:material_design_java", + "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_preference_preference_java", ++ "//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java", + "//third_party/metrics_proto:metrics_proto_java", + "//ui/android:ui_full_java", + "//ui/android:ui_utils_java", +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarFocusScrimHandler.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarFocusScrimHandler.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarFocusScrimHandler.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarFocusScrimHandler.java +@@ -17,6 +17,9 @@ import org.chromium.components.browser_ui.widget.scrim.ScrimProperties; + import org.chromium.ui.base.DeviceFormFactor; + import org.chromium.ui.modelutil.PropertyModel; + import org.chromium.ui.util.ColorUtils; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + + /** + * Handles showing and hiding a scrim when url bar focus changes. +@@ -56,6 +59,10 @@ public class LocationBarFocusScrimHandler implements UrlFocusChangeListener { + + Resources resources = context.getResources(); + int topMargin = resources.getDimensionPixelSize(R.dimen.tab_strip_height); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // since the top bar is at the bottom, we need to cover the whole page ++ topMargin = 0; ++ } + mLightScrimColor = ApiCompatibilityUtils.getColor( + resources, R.color.omnibox_focused_fading_background_color_light); + mScrimModel = new PropertyModel.Builder(ScrimProperties.ALL_KEYS) +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java +@@ -5,6 +5,10 @@ + package org.chromium.chrome.browser.toolbar.bottom; + + import android.app.Activity; ++import org.chromium.base.supplier.ObservableSupplier; ++import org.chromium.chrome.browser.theme.TopUiThemeColorProvider; ++import org.chromium.chrome.browser.tab.CurrentTabObserver; ++import org.chromium.chrome.browser.tab.Tab; + + /** + * Interface for the bottom controls content UI. This UI delegates various operations to +@@ -22,9 +26,12 @@ public interface BottomControlsContentDelegate { + * Initialize the delegate on native initialization. + * @param activity Activity for the delegate. + * @param visibilityController Bottom controls visibility controller. ++ * @param topUiThemeColorProvider {@link ThemeColorProvider} for top UI. ++ * @param tabSupplier Activity tab supplier. + */ + void initializeWithNative(Activity activity, +- BottomControlsCoordinator.BottomControlsVisibilityController visibilityController); ++ BottomControlsCoordinator.BottomControlsVisibilityController visibilityController, ++ TopUiThemeColorProvider topUiThemeColorProvider, ObservableSupplier tabSupplier); + + /** Destroy the delegate. */ + void destroy(); +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java +@@ -23,6 +23,8 @@ import org.chromium.ui.modelutil.PropertyModel; + import org.chromium.ui.modelutil.PropertyModelChangeProcessor; + import org.chromium.ui.resources.ResourceManager; + import org.chromium.ui.widget.Toast; ++import org.chromium.chrome.browser.theme.TopUiThemeColorProvider; ++import org.chromium.chrome.browser.tab.Tab; + + /** + * The root coordinator for the bottom controls component. This component is intended for use with +@@ -57,6 +59,8 @@ public class BottomControlsCoordinator { + * @param overlayPanelVisibilitySupplier Notifies overlay panel visibility event. + * @param resourceManager A {@link ResourceManager} for loading textures into the compositor. + * @param layoutManager A {@link LayoutManagerImpl} to attach overlays to. ++ * @param topUiThemeColorProvider {@link ThemeColorProvider} for top UI. ++ * @param tabSupplier Activity tab supplier. + */ + @SuppressLint("CutPasteId") // Not actually cut and paste since it's View vs ViewGroup. + public BottomControlsCoordinator(Activity activity, WindowAndroid windowAndroid, +@@ -64,7 +68,9 @@ public class BottomControlsCoordinator { + BrowserControlsSizer controlsSizer, FullscreenManager fullscreenManager, + ScrollingBottomViewResourceFrameLayout root, + BottomControlsContentDelegate contentDelegate, +- ObservableSupplier overlayPanelVisibilitySupplier) { ++ ObservableSupplier overlayPanelVisibilitySupplier, ++ TopUiThemeColorProvider topUiThemeColorProvider, ++ ObservableSupplier tabSupplier) { + PropertyModel model = new PropertyModel(BottomControlsProperties.ALL_KEYS); + + ScrollingBottomViewSceneLayer sceneLayer = +@@ -98,7 +104,8 @@ public class BottomControlsCoordinator { + layoutManager.addSceneOverlay(sceneLayer); + + if (mContentDelegate != null) { +- mContentDelegate.initializeWithNative(activity, mMediator::setBottomControlsVisible); ++ mContentDelegate.initializeWithNative(activity, mMediator::setBottomControlsVisible, ++ topUiThemeColorProvider, tabSupplier); + } + } + +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java +@@ -15,6 +15,8 @@ import org.chromium.chrome.browser.layouts.LayoutType; + import org.chromium.ui.KeyboardVisibilityDelegate; + import org.chromium.ui.base.WindowAndroid; + import org.chromium.ui.modelutil.PropertyModel; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + /** + * This class is responsible for reacting to events from the outside world, interacting with other +@@ -95,6 +97,12 @@ class BottomControlsMediator implements BrowserControlsStateProvider.Observer, + } + + void setBottomControlsVisible(boolean visible) { ++ if (visible == true ++ && mIsBottomControlsVisible == false ++ && CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // always show the toolbar if the bottom controls are visible, so as not to leave the hole below. ++ mBrowserControlsSizer.getBrowserVisibilityDelegate().showControlsTransient(); ++ } + mIsBottomControlsVisible = visible; + updateCompositedViewVisibility(); + updateAndroidViewVisibility(); +@@ -116,6 +124,7 @@ class BottomControlsMediator implements BrowserControlsStateProvider.Observer, + @Override + public void onControlsOffsetChanged(int topOffset, int topControlsMinHeightOffset, + int bottomOffset, int bottomControlsMinHeightOffset, boolean needsAnimate) { ++ mModel.set(BottomControlsProperties.TOPCONTROLSMINHEIGHT_OFFSET, topControlsMinHeightOffset); + mModel.set(BottomControlsProperties.Y_OFFSET, bottomOffset); + updateAndroidViewVisibility(); + } +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java +@@ -16,6 +16,9 @@ class BottomControlsProperties { + /** The Y offset of the view in px. */ + static final WritableIntPropertyKey Y_OFFSET = new WritableIntPropertyKey(); + ++ /** The min height of browser controls in px. */ ++ static final WritableIntPropertyKey TOPCONTROLSMINHEIGHT_OFFSET = new WritableIntPropertyKey(); ++ + /** Whether the Android view version of the bottom controls component is visible. */ + static final WritableBooleanPropertyKey ANDROID_VIEW_VISIBLE = new WritableBooleanPropertyKey(); + +@@ -24,5 +27,5 @@ class BottomControlsProperties { + new WritableBooleanPropertyKey(); + + static final PropertyKey[] ALL_KEYS = new PropertyKey[] {BOTTOM_CONTROLS_CONTAINER_HEIGHT_PX, +- Y_OFFSET, ANDROID_VIEW_VISIBLE, COMPOSITED_VIEW_VISIBLE}; ++ Y_OFFSET, ANDROID_VIEW_VISIBLE, COMPOSITED_VIEW_VISIBLE, TOPCONTROLSMINHEIGHT_OFFSET}; + } +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java +@@ -39,6 +39,8 @@ class BottomControlsViewBinder { + model.get(BottomControlsProperties.BOTTOM_CONTROLS_CONTAINER_HEIGHT_PX); + } else if (BottomControlsProperties.Y_OFFSET == propertyKey) { + view.sceneLayer.setYOffset(model.get(BottomControlsProperties.Y_OFFSET)); ++ } else if (BottomControlsProperties.TOPCONTROLSMINHEIGHT_OFFSET == propertyKey) { ++ view.sceneLayer.setTopControlsMinHeightOffset(model.get(BottomControlsProperties.TOPCONTROLSMINHEIGHT_OFFSET)); + } else if (BottomControlsProperties.ANDROID_VIEW_VISIBLE == propertyKey) { + view.root.setVisibility(model.get(BottomControlsProperties.ANDROID_VIEW_VISIBLE) + ? View.VISIBLE +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java +@@ -19,6 +19,9 @@ import org.chromium.ui.resources.ResourceManager; + + import java.util.List; + ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** + * A composited view that sits at the bottom of the screen and listens to changes in the browser + * controls. When visible, the view will mimic the behavior of the top browser controls when +@@ -38,6 +41,9 @@ public class ScrollingBottomViewSceneLayer extends SceneOverlayLayer implements + /** The current Y offset of the bottom view in px. */ + private int mCurrentYOffsetPx; + ++ /** The min height of browser controls in px. */ ++ private int mTopControlsMinHeightOffset; ++ + /** The current X offset of the bottom view in px. */ + private int mCurrentXOffsetPx; + +@@ -85,6 +91,13 @@ public class ScrollingBottomViewSceneLayer extends SceneOverlayLayer implements + mCurrentXOffsetPx = offsetPx; + } + ++ /** ++ * @param offsetPx The min height of browser controls in px. ++ */ ++ public void setTopControlsMinHeightOffset(int offsetPx) { ++ mTopControlsMinHeightOffset = offsetPx; ++ } ++ + /** + * @param visible Whether this {@link SceneLayer} is visible. + */ +@@ -113,9 +126,14 @@ public class ScrollingBottomViewSceneLayer extends SceneOverlayLayer implements + // The composited shadow should be visible if the Android toolbar's isn't. + boolean isShadowVisible = mBottomView.getVisibility() != View.VISIBLE; + ++ float offsetPy = viewport.height() + mCurrentYOffsetPx; ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // fix the offset of the fake bottom controls, used only for animations ++ offsetPy -= (mBottomView.getHeight() - mCurrentYOffsetPx + mTopControlsMinHeightOffset); ++ } + ScrollingBottomViewSceneLayerJni.get().updateScrollingBottomViewLayer(mNativePtr, + ScrollingBottomViewSceneLayer.this, resourceManager, mResourceId, +- mTopShadowHeightPx, mCurrentXOffsetPx, viewport.height() + mCurrentYOffsetPx, ++ mTopShadowHeightPx, mCurrentXOffsetPx, offsetPy, + isShadowVisible); + + return this; +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java +@@ -34,6 +34,11 @@ import org.chromium.ui.base.ViewUtils; + import org.chromium.ui.resources.dynamics.ViewResourceAdapter; + import org.chromium.ui.widget.OptimizedFrameLayout; + ++import android.view.Gravity; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++import androidx.coordinatorlayout.widget.CoordinatorLayout; ++ + /** + * Layout for the browser controls (omnibox, menu, tab strip, etc..). + */ +@@ -90,6 +95,12 @@ public class ToolbarControlContainer extends OptimizedFrameLayout implements Con + @Override + public void initWithToolbar(int toolbarLayoutId) { + try (TraceEvent te = TraceEvent.scoped("ToolbarControlContainer.initWithToolbar")) { ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // the top toolbar is docked at the bottom ++ CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams)getLayoutParams(); ++ layoutParams.gravity = Gravity.START | Gravity.BOTTOM; ++ } ++ + mToolbarContainer = + (ToolbarViewResourceFrameLayout) findViewById(R.id.toolbar_container); + ViewStub toolbarStub = findViewById(R.id.toolbar_stub); +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayCoordinator.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayCoordinator.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayCoordinator.java +@@ -26,6 +26,9 @@ import org.chromium.ui.resources.ResourceManager; + + import java.util.List; + ++import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; ++ + /** The public interface for the top toolbar texture component. */ + public class TopToolbarOverlayCoordinator implements SceneOverlay { + /** The view state for this overlay. */ +@@ -57,6 +60,9 @@ public class TopToolbarOverlayCoordinator implements SceneOverlay { + browserControlsStateProvider.getTopControlOffset() + + browserControlsStateProvider.getTopControlsMinHeight()) + .with(TopToolbarOverlayProperties.ANONYMIZE, false) ++ .with(TopToolbarOverlayProperties.VIEWPORT_HEIGHT, 0) ++ .with(TopToolbarOverlayProperties.TOOLBAR_HEIGHT, ++ browserControlsStateProvider.getTopControlsHeight()) + .build(); + mSceneLayer = new TopToolbarSceneLayer(resourceManagerSupplier); + mChangeProcessor = +@@ -100,6 +106,7 @@ public class TopToolbarOverlayCoordinator implements SceneOverlay { + @Override + public SceneOverlayLayer getUpdatedSceneOverlayTree( + RectF viewport, RectF visibleViewport, ResourceManager resourceManager, float yOffset) { ++ mModel.set(TopToolbarOverlayProperties.VIEWPORT_HEIGHT, viewport.height()); + return mSceneLayer; + } + +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayProperties.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayProperties.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayProperties.java +@@ -46,8 +46,14 @@ public class TopToolbarOverlayProperties { + /** The current y offset of the top toolbar. */ + public static final WritableFloatPropertyKey Y_OFFSET = new WritableFloatPropertyKey(); + ++ /** The current height of the main visible view. */ ++ public static final WritableFloatPropertyKey VIEWPORT_HEIGHT = new WritableFloatPropertyKey(); ++ ++ /** The current height of the top toolbar. */ ++ public static final WritableFloatPropertyKey TOOLBAR_HEIGHT = new WritableFloatPropertyKey(); ++ + public static final PropertyKey[] ALL_KEYS = + new PropertyKey[] {ANONYMIZE, PROGRESS_BAR_INFO, RESOURCE_ID, SHOW_SHADOW, + TOOLBAR_BACKGROUND_COLOR, URL_BAR_COLOR, URL_BAR_RESOURCE_ID, VISIBLE, +- X_OFFSET, Y_OFFSET}; ++ X_OFFSET, Y_OFFSET, VIEWPORT_HEIGHT, TOOLBAR_HEIGHT}; + } +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java +@@ -14,6 +14,8 @@ import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar.Drawing + import org.chromium.ui.modelutil.PropertyKey; + import org.chromium.ui.modelutil.PropertyModel; + import org.chromium.ui.resources.ResourceManager; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.flags.CachedFeatureFlags; + + /** A SceneLayer to render the top toolbar. This is the "view" piece of the top toolbar overlay. */ + @JNINamespace("android") +@@ -39,13 +41,20 @@ class TopToolbarSceneLayer extends SceneOverlayLayer { + /** Push all information about the texture to native at once. */ + private void pushProperties(PropertyModel model) { + if (mResourceManagerSupplier.get() == null) return; ++ float offsetY = model.get(TopToolbarOverlayProperties.Y_OFFSET); ++ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM)) { ++ // fix the offset of the fake top controls, used only for animations ++ offsetY = model.get(TopToolbarOverlayProperties.VIEWPORT_HEIGHT) - ++ model.get(TopToolbarOverlayProperties.TOOLBAR_HEIGHT) - ++ offsetY; ++ } + TopToolbarSceneLayerJni.get().updateToolbarLayer(mNativePtr, TopToolbarSceneLayer.this, + mResourceManagerSupplier.get(), model.get(TopToolbarOverlayProperties.RESOURCE_ID), + model.get(TopToolbarOverlayProperties.TOOLBAR_BACKGROUND_COLOR), + model.get(TopToolbarOverlayProperties.URL_BAR_RESOURCE_ID), + model.get(TopToolbarOverlayProperties.URL_BAR_COLOR), + model.get(TopToolbarOverlayProperties.X_OFFSET), +- model.get(TopToolbarOverlayProperties.Y_OFFSET), ++ offsetY, + model.get(TopToolbarOverlayProperties.SHOW_SHADOW), + model.get(TopToolbarOverlayProperties.VISIBLE), + model.get(TopToolbarOverlayProperties.ANONYMIZE)); +diff --git a/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml b/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml +--- a/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml ++++ b/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml +@@ -39,4 +39,8 @@ + android:key="captions" + android:title="@string/accessibility_captions_title"/> + ++ + +diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java +--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java ++++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java +@@ -28,6 +28,8 @@ public class AccessibilitySettings + public static final String PREF_CAPTIONS = "captions"; + + static final String PREF_FORCE_TABLET_UI = "force_tablet_ui"; ++ static final String PREF_MOVE_TOOLBAR_TO_BOTTOM = "move_toolbar_bottom"; ++ + private TextScalePreference mTextScalePref; + private ChromeBaseCheckBoxPreference mForceEnableZoomPref; + private boolean mRecordFontSizeChangeOnStop; +@@ -35,6 +37,7 @@ public class AccessibilitySettings + private BooleanPreferenceDelegate mForceTabletUIDelegate; + private BooleanPreferenceDelegate mReaderForAccessibilityDelegate; + private BooleanPreferenceDelegate mAccessibilityTabSwitcherDelegate; ++ private BooleanPreferenceDelegate mMoveTopToolbarToBottomDelegate; + + private FontSizePrefs mFontSizePrefs; + private FontSizePrefsObserver mFontSizePrefsObserver = new FontSizePrefsObserver() { +@@ -54,6 +57,10 @@ public class AccessibilitySettings + mFontSizePrefs = FontSizePrefs.getInstance(delegate.getBrowserContextHandle()); + } + ++ public AccessibilitySettingsDelegate getDelegate() { ++ return mDelegate; ++ } ++ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); +@@ -103,6 +110,12 @@ public class AccessibilitySettings + getPreferenceScreen().removePreference(accessibilityTabSwitcherPref); + } + ++ ChromeBaseCheckBoxPreference mMoveToolbarToBottomPref = ++ (ChromeBaseCheckBoxPreference) findPreference(PREF_MOVE_TOOLBAR_TO_BOTTOM); ++ mMoveTopToolbarToBottomDelegate = mDelegate.getMoveTopToolbarToBottomDelegate(); ++ mMoveToolbarToBottomPref.setChecked(mMoveTopToolbarToBottomDelegate.isEnabled()); ++ mMoveToolbarToBottomPref.setOnPreferenceChangeListener(this); ++ + Preference captions = findPreference(PREF_CAPTIONS); + captions.setOnPreferenceClickListener(preference -> { + Intent intent = new Intent(Settings.ACTION_CAPTIONING_SETTINGS); +@@ -147,6 +160,9 @@ public class AccessibilitySettings + if (mReaderForAccessibilityDelegate != null) { + mReaderForAccessibilityDelegate.setEnabled((Boolean) newValue); + } ++ } else if (PREF_MOVE_TOOLBAR_TO_BOTTOM.equals(preference.getKey())) { ++ mMoveTopToolbarToBottomDelegate.setEnabled((Boolean) newValue); ++ mDelegate.requestRestart(getActivity()); + } + return true; + } +diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java +--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java ++++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java +@@ -4,6 +4,8 @@ + + package org.chromium.components.browser_ui.accessibility; + ++import android.app.Activity; ++ + import androidx.annotation.NonNull; + import androidx.preference.PreferenceFragmentCompat; + +@@ -27,6 +29,10 @@ public interface AccessibilitySettingsDelegate { + void setEnabled(boolean value); + } + ++ void requestRestart(Activity activity); ++ ++ BooleanPreferenceDelegate getMoveTopToolbarToBottomDelegate(); ++ + /** + * @return The BrowserContextHandle that should be used to read and update settings. + */ +diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc +--- a/content/browser/renderer_host/render_widget_host_view_android.cc ++++ b/content/browser/renderer_host/render_widget_host_view_android.cc +@@ -27,6 +27,7 @@ + #include "base/task/thread_pool.h" + #include "base/threading/scoped_blocking_call.h" + #include "base/threading/thread_task_runner_handle.h" ++#include "cc/base/features.h" + #include "cc/base/math_util.h" + #include "cc/layers/layer.h" + #include "cc/layers/surface_layer.h" +@@ -462,6 +463,8 @@ void RenderWidgetHostViewAndroid::OnRenderFrameMetadataChangedBeforeActivation( + // factor. Thus, |top_content_offset| in CSS pixels is also in DIPs. + float top_content_offset = + metadata.top_controls_height * metadata.top_controls_shown_ratio; ++ if (base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) ++ top_content_offset = 0; + float top_shown_pix = top_content_offset * to_pix; + + if (ime_adapter_android_) { +-- +2.25.1