Android: Navigation on TabSwitcher

This CL add edge-swipe navigation to Tab switcher. Swipe from left
edge exits the switcher, and swipe from right edge does nothing. An
upcoming CL will add edge glow effect to the right swipe to indicate
that no forward navigation is possible.

FrameLayout(mViewContainer) in StackLayout which is the topmost view
for tab switcher below CompositorViewHolder, is used as the container
view for HistoryNavigationLayout.

To make HistoryNavigationLayout (and below) a pure Android view
structure without Chrome-specific classes (like Tab or
ChromeActivity), introduced an interface
HistoryNavigation.NavigationHelper. Its implementation
NavigationHelperImpl is injected from various native page view. This
makes it easy to write tests based on DumyyUiActivity.

NavigationHandler.ActionDelegate is another interface that keeps
NavigationHandler free of Tab/ChromeActivity. Native/rendered pages
can share a common impl (TabbedActionDelegate). Tab switcher has its
own implementation of ActionDelegate since it doesn't depend on Tab
for navigation.

Bug: 937946
Change-Id: Id9572e39ce9c6b537e118014d6924064117492af
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1573429
Commit-Queue: Jinsuk Kim <jinsukkim@chromium.org>
Reviewed-by: Ted Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#653884}
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 6679392..a0cc022 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -709,9 +709,11 @@
   "java/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelper.java",
   "java/src/org/chromium/chrome/browser/gcore/LifecycleHook.java",
   "java/src/org/chromium/chrome/browser/gesturenav/ArrowChipView.java",
+  "java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java",
   "java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java",
   "java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java",
+  "java/src/org/chromium/chrome/browser/gesturenav/TabbedActionDelegate.java",
   "java/src/org/chromium/chrome/browser/gsa/ContextReporter.java",
   "java/src/org/chromium/chrome/browser/gsa/GSAAccountChangeListener.java",
   "java/src/org/chromium/chrome/browser/gsa/GSAContextDisplaySelection.java",
diff --git a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java
index d0d510d..d1a52ce 100644
--- a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java
+++ b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java
@@ -1119,7 +1119,7 @@
         if (mActivity instanceof ChromeTabbedActivity) {
             // If hitting back would minimize Chrome, disable the back button.
             // See ChromeTabbedActivity#handleBackPressed().
-            willCloseTab = ChromeTabbedActivity.backShouldCloseTab(mTab)
+            willCloseTab = mActivity.backShouldCloseTab(mTab)
                     && !TabAssociatedApp.isOpenedFromExternalApp(mTab);
         }
         boolean canGoBack = mTab.canGoBack() || willCloseTab;
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index 50130ea1..b27bde0d 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -284,12 +284,12 @@
     }
 
     @Override
-    protected void initializeMainView(Context context) {
+    protected void initializeMainView(Context context, NativePageHost host) {
         int topPadding = context.getResources().getDimensionPixelOffset(R.dimen.tab_strip_height);
 
         mRootView = new RootView(context, mConstructedTimeNs);
         mRootView.setPadding(0, topPadding, 0, 0);
-        mRootView.setTab(mTab);
+        mRootView.setNavigationDelegate(host.createHistoryNavigationDelegate());
         mUiConfig = new UiConfig(mRootView);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 258753a..221373c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1935,6 +1935,15 @@
      */
     protected abstract boolean handleBackPressed();
 
+    /**
+     * @return If no higher priority back actions occur, whether pressing the back button
+     *         would result in closing the tab. A true return value does not guarantee that
+     *         a subsequent call to {@link #handleBackPressed()} will close the tab.
+     */
+    public boolean backShouldCloseTab(Tab tab) {
+        return false;
+    }
+
     @Override
     public void onOrientationChange(int orientation) {
         if (mToolbarManager != null) mToolbarManager.onOrientationChange();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 07bfcad..901d479 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1989,7 +1989,8 @@
      *
      * @return Whether pressing the back button on the provided Tab should close the Tab.
      */
-    public static boolean backShouldCloseTab(Tab tab) {
+    @Override
+    public boolean backShouldCloseTab(Tab tab) {
         @TabLaunchType
         int type = tab.getLaunchType();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
index e7fb18b..8324acd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.gesturenav.NavigationHandler;
+import org.chromium.chrome.browser.gesturenav.TabbedActionDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -196,7 +197,8 @@
             return mSwipeRefreshLayout.start();
         } else if (type == OverscrollAction.HISTORY_NAVIGATION && mNavigationEnabled) {
             if (mNavigationHandler == null) {
-                mNavigationHandler = new NavigationHandler(mContainerView, () -> mTab);
+                mNavigationHandler =
+                        new NavigationHandler(mContainerView, new TabbedActionDelegate(mTab));
             }
             boolean navigable = navigateForward ? mTab.canGoForward() : mTab.canGoBack();
             boolean shouldStart = navigable || !navigateForward;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
index 383a3cb..92b3a83e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
@@ -22,11 +22,11 @@
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout.HistoryNavigationDelegate;
 import org.chromium.chrome.browser.native_page.BasicNativePage;
 import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksReader;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.util.ConversionUtils;
 import org.chromium.chrome.browser.widget.selection.SelectableListLayout;
 import org.chromium.chrome.browser.widget.selection.SelectableListToolbar.SearchDelegate;
@@ -267,11 +267,11 @@
     }
 
     /**
-     * Sets the tab this manager is running on.
-     * @param tab Tab instance.
+     * Sets the delegate object needed for history navigation logic.
+     * @param delegate {@link HistoryNavigationDelegate} object.
      */
-    public void setTab(Tab tab) {
-        mSelectableListLayout.setTab(tab);
+    public void setHistoryNavigationDelegate(HistoryNavigationDelegate delegate) {
+        mSelectableListLayout.setHistoryNavigationDelegate(delegate);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java
index 68964eb..4fe0ee2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java
@@ -35,7 +35,7 @@
         mManager = new BookmarkManager(
                 activity, false, ((SnackbarManageable) activity).getSnackbarManager());
         mManager.setBasicNativePage(this);
-        mManager.setTab(host.getActiveTab());
+        mManager.setHistoryNavigationDelegate(host.createHistoryNavigationDelegate());
         mTitle = activity.getString(R.string.bookmarks);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
index b963616..01901cf0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
@@ -10,6 +10,7 @@
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.util.Pair;
+import android.view.MotionEvent;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.widget.FrameLayout;
@@ -18,6 +19,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.compositor.LayerTitleCache;
 import org.chromium.chrome.browser.compositor.animation.CompositorAnimator;
@@ -38,6 +40,7 @@
 import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
 import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
+import org.chromium.chrome.browser.gesturenav.NavigationHandler;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabList;
@@ -159,6 +162,8 @@
     /** Rectangles that defines the area where each stack need to be laid out. */
     protected final ArrayList<RectF> mStackRects;
 
+    private final float mDpToPx;
+
     private int mStackAnimationCount;
 
     private float mFlingSpeed; // pixel/ms
@@ -225,8 +230,10 @@
 
     private final GestureEventFilter mGestureEventFilter;
     private final TabListSceneLayer mSceneLayer;
+    private final boolean mNavigationEnabled;
 
     private StackLayoutGestureHandler mGestureHandler;
+    private NavigationHandler mNavigationHandler;
 
     private final ArrayList<Pair<CompositorAnimator, FloatProperty>> mLayoutAnimations =
             new ArrayList<>();
@@ -243,6 +250,7 @@
             mLastOnDownTimeStamp = time;
 
             if (shouldIgnoreTouchInput()) return;
+            if (mNavigationEnabled) mNavigationHandler.onDown();
             mStacks.get(getTabStackIndex()).onDown(time);
         }
 
@@ -255,6 +263,15 @@
         public void drag(float x, float y, float dx, float dy, float tx, float ty) {
             if (shouldIgnoreTouchInput()) return;
 
+            if (mNavigationEnabled) {
+                mNavigationHandler.onScroll(mLastOnDownX * mDpToPx, -dx * mDpToPx, -dy * mDpToPx,
+                        x * mDpToPx, y * mDpToPx);
+                if (mNavigationHandler.isActive()) {
+                    cancelDragTabs(time());
+                    return;
+                }
+            }
+
             @SwipeMode
             int oldInputMode = mInputMode;
             long time = time();
@@ -346,6 +363,13 @@
         private void onUpOrCancel(long time) {
             if (shouldIgnoreTouchInput()) return;
 
+            if (mNavigationEnabled && mNavigationHandler.isActive()) {
+                mNavigationHandler.onTouchEvent(MotionEvent.ACTION_UP);
+            }
+            cancelDragTabs(time);
+        }
+
+        private void cancelDragTabs(long time) {
             int currentIndex = getTabStackIndex();
             if (!mClicked
                     && Math.abs(currentIndex + mRenderedScrollOffset) > THRESHOLD_TO_SWITCH_STACK) {
@@ -390,6 +414,9 @@
         mStackRects = new ArrayList<RectF>();
         mViewContainer = new FrameLayout(getContext());
         mSceneLayer = new TabListSceneLayer();
+        mNavigationEnabled =
+                ChromeFeatureList.isEnabled(ChromeFeatureList.OVERSCROLL_HISTORY_NAVIGATION);
+        mDpToPx = context.getResources().getDisplayMetrics().density;
     }
 
     /**
@@ -502,6 +529,29 @@
                 onTabClosureCancelled(LayoutManager.time(), tab.getId(), tab.isIncognito());
             }
         };
+        if (mNavigationEnabled && mNavigationHandler == null) {
+            final ChromeActivity activity = currentTab().getActivity();
+            mNavigationHandler =
+                    new NavigationHandler(mViewContainer, new NavigationHandler.ActionDelegate() {
+                        @Override
+                        public boolean canNavigate(boolean forward) {
+                            return !forward;
+                        }
+                        @Override
+                        public void navigate(boolean forward) {
+                            // Called only when !forward.
+                            activity.onBackPressed();
+                        }
+                        @Override
+                        public boolean willBackExitApp() {
+                            return currentTab() == null;
+                        }
+                    });
+        }
+    }
+
+    private Tab currentTab() {
+        return TabModelUtils.getCurrentTab(mTabModelSelector.getCurrentModel());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java
index e48a76c..85d6979 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java
@@ -51,7 +51,7 @@
                 activity.getModalDialogManager());
 
         mDownloadCoordinator.addObserver(this);
-        mDownloadCoordinator.setTab(host.getActiveTab());
+        mDownloadCoordinator.setHistoryNavigationDelegate(host.createHistoryNavigationDelegate());
         mTitle = activity.getString(R.string.menu_downloads);
 
         // #destroy() unregisters the ActivityStateListener to avoid checking for externally removed
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinator.java
index bacde1a..95b64ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinator.java
@@ -6,7 +6,7 @@
 
 import android.view.View;
 
-import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout.HistoryNavigationDelegate;
 
 /**
  * A coordinator that represents the main download manager UI page. This visually shows a list of
@@ -49,6 +49,9 @@
     /** Stops notifying {@code observer} of url state changes. */
     void removeObserver(Observer observer);
 
-    /** Sets the {@link Tab} object that the manager is working on. */
-    void setTab(Tab tab);
+    /**
+     * Sets the {@link HistoryNavigationDelegate} object that takes care of history navigation.
+     * @param delegate The delegate instance the history navigation logic needs.
+     */
+    void setHistoryNavigationDelegate(HistoryNavigationDelegate delegate);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
index 6f9f274..829cc3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
@@ -21,11 +21,11 @@
 import org.chromium.chrome.browser.download.home.toolbar.ToolbarCoordinator;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout.HistoryNavigationDelegate;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.download.DownloadPreferences;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.chrome.download.R;
 import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -155,6 +155,11 @@
         mObservers.removeObserver(observer);
     }
 
+    @Override
+    public void setHistoryNavigationDelegate(HistoryNavigationDelegate delegate) {
+        mMainView.setNavigationDelegate(delegate);
+    }
+
     // ToolbarActionDelegate implementation.
     @Override
     public void close() {
@@ -167,11 +172,6 @@
         PreferencesLauncher.launchSettingsPage(mActivity, DownloadPreferences.class);
     }
 
-    @Override
-    public void setTab(Tab tab) {
-        mMainView.setTab(tab);
-    }
-
     private void notifyFilterChanged(@FilterType int filter) {
         mSelectionDelegate.clearSelection();
         if (mMuteFilterChanges) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index a201d9f..904bf03 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -37,6 +37,7 @@
 import org.chromium.chrome.browser.download.home.metrics.UmaUtils.MenuAction;
 import org.chromium.chrome.browser.download.home.toolbar.ToolbarUtils;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout.HistoryNavigationDelegate;
 import org.chromium.chrome.browser.native_page.BasicNativePage;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.download.DownloadPreferences;
@@ -44,7 +45,6 @@
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.widget.ThumbnailProvider;
 import org.chromium.chrome.browser.widget.ThumbnailProviderImpl;
 import org.chromium.chrome.browser.widget.selection.SelectableListLayout;
@@ -344,7 +344,7 @@
     }
 
     @Override
-    public void setTab(Tab tab) {}
+    public void setHistoryNavigationDelegate(HistoryNavigationDelegate delegate) {}
 
     @Override
     public boolean onMenuItemClick(MenuItem item) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
index ba5f056..c125d1d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
@@ -159,7 +159,7 @@
         CategoryCardAdapter adapterDelegate = new CategoryCardAdapter(
                 mModel, mLayoutManager, iconGenerator, mContextMenuManager, navDelegate, mProfile);
 
-        mView.setTab(mTab);
+        mView.setNavigationDelegate(host.createHistoryNavigationDelegate());
         mRecyclerView = mView.findViewById(R.id.explore_sites_category_recycler);
 
         CategoryCardViewHolderFactory factory = FeatureUtilities.isNoTouchModeEnabled()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationDelegateImpl.java
new file mode 100644
index 0000000..840e840
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationDelegateImpl.java
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.gesturenav;
+
+import android.content.Context;
+
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.tab.Tab;
+
+/**
+ * {@link HistoryNavigationLayout#HistoryNavigationDelegate} implementation for
+ * native pages in tabbed mode.
+ */
+public class HistoryNavigationDelegateImpl
+        implements HistoryNavigationLayout.HistoryNavigationDelegate {
+    private final Tab mTab;
+    private final boolean mIsEnabled;
+    private final boolean mDelegateSwipes;
+
+    public HistoryNavigationDelegateImpl(Context context, Tab tab) {
+        mTab = tab;
+        mIsEnabled = ChromeFeatureList.isEnabled(ChromeFeatureList.OVERSCROLL_HISTORY_NAVIGATION)
+                && (context instanceof ChromeActivity);
+        mDelegateSwipes = ChromeFeatureList.isEnabled(ChromeFeatureList.DELEGATE_OVERSCROLL_SWIPES);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    @Override
+    public boolean delegateSwipes() {
+        return mDelegateSwipes;
+    }
+
+    @Override
+    public NavigationHandler.ActionDelegate createActionDelegate() {
+        return new TabbedActionDelegate(mTab);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java
index 60fda4a..b3beb69 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java
@@ -10,45 +10,58 @@
 import android.view.MotionEvent;
 import android.widget.FrameLayout;
 
-import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.tab.Tab;
-
 /**
  * FrameLayout that supports side-wise slide gesture for history navigation. Inheriting
  * class may need to override {@link #wasLastSideSwipeGestureConsumed()} if
  * {@link #onTouchEvent} cannot be relied upon to know whether the side-swipe related
  * event was handled. Namely {@link android.support.v7.widget.RecyclerView}) always
  * claims to handle touch events.
+ * TODO(jinsukkim): Write a test verifying UI logic.
  */
 public class HistoryNavigationLayout extends FrameLayout {
-    private final boolean mDelegateSwipes;
-    private final boolean mNavigationEnabled;
+    private boolean mDelegateSwipes;
+    private boolean mNavigationEnabled;
     private GestureDetector mDetector;
     private NavigationHandler mNavigationHandler;
 
+    /**
+     * Interface providing navigation-related configuration for native pages using this class.
+     */
+    public interface HistoryNavigationDelegate {
+        /**
+         * @return {@code true} if history navigation is enabled.
+         */
+        boolean isEnabled();
+
+        /**
+         * @return {@code true} if swipe events are delegated to websites first.
+         */
+        boolean delegateSwipes();
+
+        /**
+         * @return {@link NavigationHandler#ActionDelegate} object.
+         */
+        NavigationHandler.ActionDelegate createActionDelegate();
+    }
+
     public HistoryNavigationLayout(Context context) {
         this(context, null);
     }
 
     public HistoryNavigationLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mDelegateSwipes = ChromeFeatureList.isEnabled(ChromeFeatureList.DELEGATE_OVERSCROLL_SWIPES);
-        mNavigationEnabled =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.OVERSCROLL_HISTORY_NAVIGATION)
-                && (context instanceof ChromeActivity);
     }
 
     /**
-     * Sets the tab which the native page for this layout is running on.
-     * @param tab Tab instance.
-     * TODO(jinsukkim): Look into replacing tab with an interface which will make this a pure
-     *     UI class. Then navigation tests will also be a pure UI one using a dummy activity.
+     * Initializes navigation logic and internal objects if navigation is enabled.
+     * @param delegate {@link HistoryNavigationDelegate} providing info and a factory method.
      */
-    public void setTab(Tab tab) {
+    public void setNavigationDelegate(HistoryNavigationDelegate delegate) {
+        mNavigationEnabled = delegate.isEnabled();
         if (!mNavigationEnabled) return;
+        mDelegateSwipes = delegate.delegateSwipes();
         mDetector = new GestureDetector(getContext(), new SideNavGestureListener());
-        mNavigationHandler = new NavigationHandler(this, () -> tab);
+        mNavigationHandler = new NavigationHandler(this, delegate.createActionDelegate());
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java
index c90601cc..3beed66 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java
@@ -10,10 +10,7 @@
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 
-import org.chromium.base.Supplier;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.tab.Tab;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -43,7 +40,6 @@
     }
 
     private final ViewGroup mParentView;
-    private final Supplier<Tab> mCurrentTab;
 
     private @GestureState int mState = GestureState.NONE;
 
@@ -58,9 +54,32 @@
     // it does not conflict with pending Android draws.
     private Runnable mDetachLayoutRunnable;
 
-    public NavigationHandler(ViewGroup parentView, Supplier<Tab> tabProvider) {
+    /**
+     * Interface to perform actions for navigating.
+     */
+    public interface ActionDelegate {
+        /**
+         * @param forward Direction to navigate. {@code true} if forward.
+         * @return {@code true} if navigation toward the given direction is possible.
+         */
+        boolean canNavigate(boolean forward);
+
+        /**
+         * Execute navigation toward the given direction.
+         * @param forward Direction to navigate. {@code true} if forward.
+         */
+        void navigate(boolean forward);
+
+        /**
+         * @return {@code true} if back action will cause the app to exit.
+         */
+        boolean willBackExitApp();
+    }
+    private final ActionDelegate mDelegate;
+
+    public NavigationHandler(ViewGroup parentView, ActionDelegate delegate) {
         mParentView = parentView;
-        mCurrentTab = tabProvider;
+        mDelegate = delegate;
         mEdgeWidthPx = EDGE_WIDTH_DP * parentView.getResources().getDisplayMetrics().density;
     }
 
@@ -69,13 +88,7 @@
         mSideSlideLayout.setLayoutParams(
                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
         mSideSlideLayout.setOnNavigationListener((forward) -> {
-            Tab tab = mCurrentTab.get();
-            if (forward) {
-                tab.goForward();
-            } else {
-                // TODO(jinsukkim): Consider removing the direct dependency on Tab/Activity.
-                tab.getActivity().onBackPressed();
-            }
+            mDelegate.navigate(forward);
             cancelStopNavigatingRunnable();
             mSideSlideLayout.post(getStopNavigatingRunnable());
         });
@@ -125,7 +138,7 @@
         if (mState == GestureState.STARTED) {
             if (shouldTriggerUi(startX, distanceX, distanceY)) {
                 boolean forward = distanceX > 0;
-                if (canNavigate(forward) || !forward) {
+                if (mDelegate.canNavigate(forward)) {
                     showArrowWidget(forward);
                     mState = GestureState.DRAGGED;
                 }
@@ -141,11 +154,6 @@
                 && (sX < mEdgeWidthPx || (mParentView.getWidth() - mEdgeWidthPx) < sX);
     }
 
-    private boolean canNavigate(boolean forward) {
-        Tab tab = mCurrentTab.get();
-        return forward ? tab.canGoForward() : tab.canGoBack();
-    }
-
     public void showArrowWidget(boolean forward) {
         if (mSideSlideLayout == null) createLayout();
         mSideSlideLayout.setEnabled(true);
@@ -158,13 +166,7 @@
     private boolean shouldShowCloseIndicator(boolean forward) {
         // Some tabs, upon back at the beginning of the history stack, should be just closed
         // than closing the entire app. In such case we do not show the close indicator.
-        final boolean back = false;
-        return !forward && !canNavigate(back) && willBackExitApp();
-    }
-
-    private boolean willBackExitApp() {
-        boolean inTabbedMode = mCurrentTab.get().getActivity() instanceof ChromeTabbedActivity;
-        return inTabbedMode ? !ChromeTabbedActivity.backShouldCloseTab(mCurrentTab.get()) : true;
+        return !forward && mDelegate.willBackExitApp();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedActionDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedActionDelegate.java
new file mode 100644
index 0000000..803d70a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedActionDelegate.java
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.gesturenav;
+
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.tab.Tab;
+
+/**
+ * Implementation of {@link NavigationHandler#ActionDelegate} that works with
+ * native/rendered pages in tabbed mode. Uses interface methods of {@link Tab}
+ * and {@link ChromeActivity} to implement navigation.
+ */
+public class TabbedActionDelegate implements NavigationHandler.ActionDelegate {
+    private final Tab mTab;
+    private final ChromeActivity mActivity;
+
+    public TabbedActionDelegate(Tab tab) {
+        mTab = tab;
+        mActivity = tab.getActivity();
+    }
+
+    @Override
+    public boolean canNavigate(boolean forward) {
+        return forward ? mTab.canGoForward() : true;
+    }
+
+    @Override
+    public void navigate(boolean forward) {
+        if (forward) {
+            mTab.goForward();
+        } else {
+            mActivity.onBackPressed();
+        }
+    }
+
+    @Override
+    public boolean willBackExitApp() {
+        return !mTab.canGoBack() && !mTab.getActivity().backShouldCloseTab(mTab);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
index dc161c2..0493699 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -31,6 +31,7 @@
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout.HistoryNavigationDelegate;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefChangeRegistrar;
 import org.chromium.chrome.browser.preferences.PrefChangeRegistrar.PrefObserver;
@@ -42,7 +43,6 @@
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
 import org.chromium.chrome.browser.util.ConversionUtils;
@@ -282,11 +282,11 @@
     }
 
     /**
-     * Sets the tab this manager is running on.
-     * @param tab Tab instance.
+     * Sets the delegate object needed for history navigation logic.
+     * @param delegate {@link HistoryNavigationDelegate} object.
      */
-    public void setTab(Tab tab) {
-        mSelectableListLayout.setTab(tab);
+    public void setHistoryNavigationDelegate(HistoryNavigationDelegate delegate) {
+        mSelectableListLayout.setHistoryNavigationDelegate(delegate);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java
index af0f5bd..e08a230 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java
@@ -37,7 +37,7 @@
         mHistoryManager = new HistoryManager(activity, false,
                 ((SnackbarManageable) activity).getSnackbarManager(), host.isIncognito());
         mTitle = activity.getString(R.string.menu_history);
-        mHistoryManager.setTab(host.getActiveTab());
+        mHistoryManager.setHistoryNavigationDelegate(host.createHistoryNavigationDelegate());
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
index 2e0246a..4dd1384 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
@@ -16,6 +16,8 @@
 import org.chromium.chrome.browser.download.DownloadPage;
 import org.chromium.chrome.browser.explore_sites.ExploreSitesPage;
 import org.chromium.chrome.browser.feed.FeedNewTabPage;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationDelegateImpl;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout.HistoryNavigationDelegate;
 import org.chromium.chrome.browser.history.HistoryPage;
 import org.chromium.chrome.browser.ntp.IncognitoNewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPage;
@@ -79,7 +81,7 @@
         protected NativePage buildRecentTabsPage(ChromeActivity activity, Tab tab) {
             RecentTabsManager recentTabsManager =
                     new RecentTabsManager(tab, tab.getProfile(), activity);
-            return new RecentTabsPage(activity, recentTabsManager);
+            return new RecentTabsPage(activity, recentTabsManager, new TabShim(tab));
         }
     }
 
@@ -239,5 +241,10 @@
         public boolean isVisible() {
             return mTab == TabModelSelector.from(mTab).getCurrentTab();
         }
+
+        @Override
+        public HistoryNavigationDelegate createHistoryNavigationDelegate() {
+            return new HistoryNavigationDelegateImpl(mTab.getActivity(), mTab);
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageHost.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageHost.java
index ea8d2ca..2cc67e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageHost.java
@@ -6,6 +6,7 @@
 
 import android.support.annotation.Nullable;
 
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout.HistoryNavigationDelegate;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.LoadUrlParams;
 
@@ -48,4 +49,10 @@
 
     /** @return whether the hosted native page is currently visible. */
     boolean isVisible();
+
+    /**
+     * Creates a delegate object needed for history navigation logic.
+     * @return {@link HistoryNavigationDelegate} implementation.
+     */
+    HistoryNavigationDelegate createHistoryNavigationDelegate();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java
index fe28a9d..79e27fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java
@@ -88,7 +88,7 @@
         mIncognitoNewTabPageView =
                 (IncognitoNewTabPageView) inflater.inflate(R.layout.new_tab_page_incognito, null);
         mIncognitoNewTabPageView.initialize(mIncognitoNewTabPageManager);
-        mIncognitoNewTabPageView.setTab(host.getActiveTab());
+        mIncognitoNewTabPageView.setNavigationDelegate(host.createHistoryNavigationDelegate());
 
         boolean useAlternateIncognitoStrings =
                 ChromeFeatureList.isEnabled(ChromeFeatureList.INCOGNITO_STRINGS);
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
index 19ceeced..62de688 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -336,7 +336,7 @@
         mTab.addObserver(mTabObserver);
         updateSearchProviderHasLogo();
 
-        initializeMainView(activity);
+        initializeMainView(activity, nativePageHost);
 
         mFullscreenManager = activity.getFullscreenManager();
         getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@@ -365,8 +365,9 @@
     /**
      * Create and initialize the main view contained in this NewTabPage.
      * @param context The context used to inflate the view.
+     * @param host NativePageHost used for initialization.
      */
-    protected void initializeMainView(Context context) {
+    protected void initializeMainView(Context context, NativePageHost host) {
         LayoutInflater inflater = LayoutInflater.from(context);
         mNewTabPageView = (NewTabPageView) inflater.inflate(R.layout.new_tab_page_view, null);
         mNewTabPageLayout = mNewTabPageView.getNewTabPageLayout();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index 4e47184..299ac51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -17,6 +17,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.layouts.content.InvalidationAwareThumbnailProvider;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationDelegateImpl;
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.NewTabPage.FakeboxDelegate;
@@ -127,7 +128,7 @@
                 mRecyclerView::setTouchEnabled, closeContextMenuCallback,
                 NewTabPage.CONTEXT_MENU_USER_ACTION_PREFIX);
         mTab.getWindowAndroid().addContextMenuCloseListener(mContextMenuManager);
-        setTab(mTab);
+        setNavigationDelegate(new HistoryNavigationDelegateImpl(getContext(), mTab));
 
         mNewTabPageLayout.initialize(manager, tab, tileGroupDelegate, searchProviderHasLogo,
                 searchProviderIsGoogle, mRecyclerView, mContextMenuManager, mUiConfig);
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
index 066e55f..e53101b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
@@ -26,6 +26,7 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
 import org.chromium.chrome.browser.native_page.NativePage;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.ui.base.DeviceFormFactor;
 
@@ -48,6 +49,7 @@
 
     private RecentTabsManager mRecentTabsManager;
     private RecentTabsRowAdapter mAdapter;
+    private NativePageHost mPageHost;
 
     private boolean mSnapshotContentChanged;
     private int mSnapshotListPosition;
@@ -77,10 +79,13 @@
      *
      * @param activity The activity this view belongs to.
      * @param recentTabsManager The RecentTabsManager which provides the model data.
+     * @param pageHost The NativePageHost used to provide a history navigation delegate object.
      */
-    public RecentTabsPage(ChromeActivity activity, RecentTabsManager recentTabsManager) {
+    public RecentTabsPage(
+            ChromeActivity activity, RecentTabsManager recentTabsManager, NativePageHost pageHost) {
         mActivity = activity;
         mRecentTabsManager = recentTabsManager;
+        mPageHost = pageHost;
         Resources resources = activity.getResources();
 
         mTitle = resources.getString(R.string.recent_tabs);
@@ -108,7 +113,7 @@
             mFullscreenManager = null;
         }
 
-        mView.setTab(recentTabsManager.activeTab());
+        mView.setNavigationDelegate(mPageHost.createHistoryNavigationDelegate());
         onUpdated();
     }
 
@@ -171,6 +176,7 @@
         assert !mIsAttachedToWindow : "Destroy called before removed from window";
         mRecentTabsManager.destroy();
         mRecentTabsManager = null;
+        mPageHost = null;
         mAdapter.notifyDataSetInvalidated();
         mAdapter = null;
         mListView.setAdapter((RecentTabsRowAdapter) null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContentViewDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContentViewDelegate.java
index 8df14e9..465c1c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContentViewDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContentViewDelegate.java
@@ -8,6 +8,7 @@
 import android.view.MotionEvent;
 
 import org.chromium.chrome.browser.gesturenav.NavigationHandler;
+import org.chromium.chrome.browser.gesturenav.TabbedActionDelegate;
 import org.chromium.components.embedder_support.view.ContentView;
 import org.chromium.content_public.browser.WebContents;
 
@@ -55,7 +56,7 @@
         ContentView parent = (ContentView) mTab.getContentView();
         parent.setTouchEventDelegate(this);
         mGestureDetector = new GestureDetector(parent.getContext(), new SideNavGestureListener());
-        mNavigationHandler = new NavigationHandler(parent, () -> mTab);
+        mNavigationHandler = new NavigationHandler(parent, new TabbedActionDelegate(mTab));
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index 9382b73..ca7b2fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -32,6 +32,7 @@
 import org.chromium.chrome.browser.TabLoadStatus;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout.HistoryNavigationDelegate;
 import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabBrowserControlsState;
@@ -757,6 +758,12 @@
     }
 
     @Override
+    public HistoryNavigationDelegate createHistoryNavigationDelegate() {
+        assert false : "BottomSheet does not need HistoryNavigationDelegate";
+        return null;
+    }
+
+    @Override
     public boolean isContentScrolledToTop() {
         return mSheetContent == null || mSheetContent.getVerticalScrollOffset() <= 0;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
index 557b9c5..211ee67 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
@@ -27,7 +27,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
-import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout.HistoryNavigationDelegate;
 import org.chromium.chrome.browser.widget.FadingShadow;
 import org.chromium.chrome.browser.widget.FadingShadowView;
 import org.chromium.chrome.browser.widget.LoadingView;
@@ -299,10 +299,12 @@
     }
 
     /**
-     * Sets the tab the page including this layout is running on.
+     * Sets the delegate object needed for history navigation logic.
+     * @param delegate {@link HistoryNavigationDelegate} object.
      */
-    public void setTab(Tab tab) {
-        ((HistoryNavigationLayout) findViewById(R.id.list_content)).setTab(tab);
+    public void setHistoryNavigationDelegate(HistoryNavigationDelegate delegate) {
+        HistoryNavigationLayout layout = (HistoryNavigationLayout) findViewById(R.id.list_content);
+        layout.setNavigationDelegate(delegate);
     }
 
     /**