Create the base Custom Context Menu Dialog.

This CL creates the new Context Menu. There are a few key decisions I
made during each moment and if the explanation isn't in here, please
take a look at the design doc under caveats or just talk to me! (Usually
we can get things cleared up a little quicker through in person chat
than through conversations).

This change covers the base dialog and the feature flag for it. Please
take a look when you can. Image loading, Share Helper, and contextmenu
-> context_menu will all come in different cls.

----Screenshots----
https://drive.google.com/open?id=0B6D5A57VLDpeSHZfNkgzUlVhYlk
https://drive.google.com/open?id=0B6D5A57VLDpeVDNHUXFOVzVnbFU

Video - https://drive.google.com/open?id=0B6D5A57VLDpeaUNVNXFReVNnYUk

DESIGN_DOC=
https://docs.google.com/document/d/1MvLS1sRJ6CqOdSg8sKzA0j2gbKO2XGRAb02SPlK1cbw/edit#heading=h.xgjl2srtytjt

BUG=613357

Review-Url: https://codereview.chromium.org/2751333006
Cr-Commit-Position: refs/heads/master@{#460664}
diff --git a/chrome/android/java/res/drawable-hdpi/context_menu_add_to_contacts.png b/chrome/android/java/res/drawable-hdpi/context_menu_add_to_contacts.png
new file mode 100644
index 0000000..50f01e6
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/context_menu_add_to_contacts.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/context_menu_load_image.png b/chrome/android/java/res/drawable-hdpi/context_menu_load_image.png
new file mode 100644
index 0000000..b67131f
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/context_menu_load_image.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/context_menu_new_tab.png b/chrome/android/java/res/drawable-hdpi/context_menu_new_tab.png
new file mode 100644
index 0000000..1d60579
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/context_menu_new_tab.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/context_menu_add_to_contacts.png b/chrome/android/java/res/drawable-mdpi/context_menu_add_to_contacts.png
new file mode 100644
index 0000000..7760780
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/context_menu_add_to_contacts.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/context_menu_load_image.png b/chrome/android/java/res/drawable-mdpi/context_menu_load_image.png
new file mode 100644
index 0000000..2afed1b
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/context_menu_load_image.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/context_menu_new_tab.png b/chrome/android/java/res/drawable-mdpi/context_menu_new_tab.png
new file mode 100644
index 0000000..392b7d4
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/context_menu_new_tab.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/context_menu_add_to_contacts.png b/chrome/android/java/res/drawable-xhdpi/context_menu_add_to_contacts.png
new file mode 100644
index 0000000..8cf90cf
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/context_menu_add_to_contacts.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/context_menu_load_image.png b/chrome/android/java/res/drawable-xhdpi/context_menu_load_image.png
new file mode 100644
index 0000000..8ea6af2
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/context_menu_load_image.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/context_menu_new_tab.png b/chrome/android/java/res/drawable-xhdpi/context_menu_new_tab.png
new file mode 100644
index 0000000..fb2a2b4
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/context_menu_new_tab.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/context_menu_add_to_contacts.png b/chrome/android/java/res/drawable-xxhdpi/context_menu_add_to_contacts.png
new file mode 100644
index 0000000..de426c6
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/context_menu_add_to_contacts.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/context_menu_load_image.png b/chrome/android/java/res/drawable-xxhdpi/context_menu_load_image.png
new file mode 100644
index 0000000..f589079
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/context_menu_load_image.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/context_menu_new_tab.png b/chrome/android/java/res/drawable-xxhdpi/context_menu_new_tab.png
new file mode 100644
index 0000000..92f601c
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/context_menu_new_tab.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/context_menu_add_to_contacts.png b/chrome/android/java/res/drawable-xxxhdpi/context_menu_add_to_contacts.png
new file mode 100644
index 0000000..7c31254a
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/context_menu_add_to_contacts.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/context_menu_load_image.png b/chrome/android/java/res/drawable-xxxhdpi/context_menu_load_image.png
new file mode 100644
index 0000000..93b7437
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/context_menu_load_image.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/context_menu_new_tab.png b/chrome/android/java/res/drawable-xxxhdpi/context_menu_new_tab.png
new file mode 100644
index 0000000..5647efc2
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/context_menu_new_tab.png
Binary files differ
diff --git a/chrome/android/java/res/layout/tabular_context_menu.xml b/chrome/android/java/res/layout/tabular_context_menu.xml
new file mode 100644
index 0000000..11998c6
--- /dev/null
+++ b/chrome/android/java/res/layout/tabular_context_menu.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 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. -->
+<org.chromium.chrome.browser.contextmenu.TabularContextMenuViewPager
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/custom_pager"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="20dp"
+    android:layout_marginBottom="20dp"
+    android:minWidth="280dp">
+    <android.support.design.widget.TabLayout
+        android:id="@+id/tab_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/google_grey_100"
+        app:tabTextColor="#80000000"
+        app:tabSelectedTextColor="@color/light_active_color"
+        app:tabIndicatorColor="@color/light_active_color"
+        app:tabGravity="fill"
+        app:tabMode="fixed" />
+</org.chromium.chrome.browser.contextmenu.TabularContextMenuViewPager>
diff --git a/chrome/android/java/res/layout/tabular_context_menu_page.xml b/chrome/android/java/res/layout/tabular_context_menu_page.xml
new file mode 100644
index 0000000..fcf46296
--- /dev/null
+++ b/chrome/android/java/res/layout/tabular_context_menu_page.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 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. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content_layer"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:minWidth="280dp"
+    android:orientation="vertical">
+    <TextView
+        android:id="@+id/context_header_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dp"
+        android:layout_marginBottom="10dp"
+        android:layout_marginStart="20dp"
+        android:layout_marginEnd="20dp"
+        android:gravity="center"
+        android:maxLines="1"
+        android:textSize="13sp"
+        android:textStyle="bold"
+        android:ellipsize="end"
+        android:visibility="gone"/>
+    <View
+        android:id="@+id/context_divider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="#1e000000"/>
+    <ListView
+        android:id="@+id/selectable_items"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        android:dividerHeight="0dp"
+        android:divider="@null"/>
+</LinearLayout>
diff --git a/chrome/android/java/res/layout/tabular_context_menu_row.xml b/chrome/android/java/res/layout/tabular_context_menu_row.xml
new file mode 100644
index 0000000..fe1aa32
--- /dev/null
+++ b/chrome/android/java/res/layout/tabular_context_menu_row.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 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. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="15dp"
+    android:paddingEnd="15dp"
+    android:minHeight="40dp"
+    tools:ignore="UseCompoundDrawables">
+    <ImageView
+        android:id="@+id/context_menu_icon"
+        android:layout_width="20dp"
+        android:layout_height="20dp"
+        android:layout_gravity="center_vertical"
+        android:contentDescription="@null" />
+    <TextView
+        android:id="@+id/context_text"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="10dp"
+        android:layout_weight="1"
+        android:textSize="15sp"
+        android:textColor="#dd000000" />
+</LinearLayout>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index c39d6f6d..5ca6a1e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -127,6 +127,7 @@
     public static final String CONSISTENT_OMNIBOX_GEOLOCATION = "ConsistentOmniboxGeolocation";
     public static final String CONTEXTUAL_SEARCH_SINGLE_ACTIONS = "ContextualSearchSingleActions";
     public static final String CONTEXTUAL_SEARCH_URL_ACTIONS = "ContextualSearchUrlActions";
+    public static final String CUSTOM_CONTEXT_MENU = "CustomContextMenu";
     public static final String CUSTOM_FEEDBACK_UI = "CustomFeedbackUi";
     /** Whether we show an important sites dialog in the "Clear Browsing Data" flow. */
     public static final String IMPORTANT_SITES_IN_CBD = "ImportantSitesInCBD";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index 615b7c8..dbb62b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.contextmenu;
 
 import android.app.Activity;
+import android.content.Context;
 import android.util.Pair;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
@@ -15,6 +16,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content_public.browser.WebContents;
@@ -31,7 +33,10 @@
 
     private ContextMenuPopulator mPopulator;
     private ContextMenuParams mCurrentContextMenuParams;
-    private Activity mActivity;
+    private Context mContext;
+    private Callback<Integer> mCallback;
+    private Runnable mOnMenuShown;
+    private Runnable mOnMenuClosed;
 
     private ContextMenuHelper(long nativeContextMenuHelper) {
         mNativeContextMenuHelper = nativeContextMenuHelper;
@@ -64,31 +69,60 @@
      * @param params          The {@link ContextMenuParams} that indicate what menu items to show.
      */
     @CalledByNative
-    private void showContextMenu(ContentViewCore contentViewCore, ContextMenuParams params) {
+    private void showContextMenu(final ContentViewCore contentViewCore, ContextMenuParams params) {
         if (params.isFile()) return;
         View view = contentViewCore.getContainerView();
         final WindowAndroid windowAndroid = contentViewCore.getWindowAndroid();
 
         if (view == null || view.getVisibility() != View.VISIBLE || view.getParent() == null
-                || windowAndroid == null || windowAndroid.getActivity().get() == null) {
+                || windowAndroid == null || windowAndroid.getContext().get() == null
+                || mPopulator == null) {
             return;
         }
 
         mCurrentContextMenuParams = params;
-        mActivity = windowAndroid.getActivity().get();
+        mContext = windowAndroid.getContext().get();
+        mCallback = new Callback<Integer>() {
+            @Override
+            public void onResult(Integer result) {
+                mPopulator.onItemSelected(
+                        ContextMenuHelper.this, mCurrentContextMenuParams, result);
+            }
+        };
+        mOnMenuShown = new Runnable() {
+            @Override
+            public void run() {
+                WebContents webContents = contentViewCore.getWebContents();
+                RecordHistogram.recordBooleanHistogram("ContextMenu.Shown", webContents != null);
+            }
+        };
+        mOnMenuClosed = new Runnable() {
+            @Override
+            public void run() {
+                if (mNativeContextMenuHelper == 0) return;
+                nativeOnContextMenuClosed(mNativeContextMenuHelper);
+            }
+        };
 
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.CUSTOM_CONTEXT_MENU)) {
+            List<Pair<Integer, List<ContextMenuItem>>> items =
+                    mPopulator.buildContextMenu(null, mContext, mCurrentContextMenuParams);
+
+            ContextMenuUi menuUi = new TabularContextMenuUi();
+            menuUi.displayMenu(mContext, mCurrentContextMenuParams, items, mCallback, mOnMenuShown,
+                    mOnMenuClosed);
+            return;
+        }
+
+        // The Platform Context Menu requires the listener within this hepler since this helper and
+        // provides context menu for us to show.
         view.setOnCreateContextMenuListener(this);
         if (view.showContextMenu()) {
-            WebContents webContents = contentViewCore.getWebContents();
-            RecordHistogram.recordBooleanHistogram(
-                    "ContextMenu.Shown", webContents != null);
-
+            mOnMenuShown.run();
             windowAndroid.addContextMenuCloseListener(new OnCloseContextMenuListener() {
                 @Override
                 public void onContextMenuClosed() {
-                    if (mNativeContextMenuHelper == 0) return;
-
-                    nativeOnContextMenuClosed(mNativeContextMenuHelper);
+                    mOnMenuClosed.run();
                     windowAndroid.removeContextMenuCloseListener(this);
                 }
             });
@@ -132,18 +166,11 @@
 
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
-        assert mPopulator != null;
-
         List<Pair<Integer, List<ContextMenuItem>>> items =
                 mPopulator.buildContextMenu(menu, v.getContext(), mCurrentContextMenuParams);
         ContextMenuUi menuUi = new PlatformContextMenuUi(menu);
-        menuUi.displayMenu(mActivity, mCurrentContextMenuParams, items, new Callback<Integer>() {
-            @Override
-            public void onResult(Integer result) {
-                mPopulator.onItemSelected(
-                        ContextMenuHelper.this, mCurrentContextMenuParams, result);
-            }
-        });
+        menuUi.displayMenu(
+                mContext, mCurrentContextMenuParams, items, mCallback, mOnMenuShown, mOnMenuClosed);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItem.java
index 32e7241..1f9763d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItem.java
@@ -5,66 +5,85 @@
 package org.chromium.chrome.browser.contextmenu;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.IdRes;
 import android.support.annotation.StringRes;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.DefaultBrowserInfo;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
+import org.chromium.chrome.browser.widget.TintedDrawable;
 
 /**
  * List of all Context Menu Items available in Chrome.
  */
 public enum ContextMenuItem {
     // Custom Tab Group
-    OPEN_IN_NEW_CHROME_TAB(R.string.contextmenu_open_in_new_chrome_tab,
-            R.id.contextmenu_open_in_new_chrome_tab),
-    OPEN_IN_CHROME_INCOGNITO_TAB(R.string.contextmenu_open_in_chrome_incognito_tab,
+    OPEN_IN_NEW_CHROME_TAB(R.drawable.context_menu_new_tab,
+            R.string.contextmenu_open_in_new_chrome_tab, R.id.contextmenu_open_in_new_chrome_tab),
+    OPEN_IN_CHROME_INCOGNITO_TAB(R.drawable.incognito_statusbar,
+            R.string.contextmenu_open_in_chrome_incognito_tab,
             R.id.contextmenu_open_in_chrome_incognito_tab),
-    OPEN_IN_BROWSER_ID(0, R.id.contextmenu_open_in_browser_id),
+    OPEN_IN_BROWSER_ID(R.drawable.context_menu_new_tab, 0, R.id.contextmenu_open_in_browser_id),
 
     // Link Group
-    OPEN_IN_OTHER_WINDOW(R.string.contextmenu_open_in_other_window,
+    OPEN_IN_OTHER_WINDOW(R.drawable.context_menu_new_tab, R.string.contextmenu_open_in_other_window,
             R.id.contextmenu_open_in_other_window),
-    OPEN_IN_NEW_TAB(R.string.contextmenu_open_in_new_tab, R.id.contextmenu_open_in_new_tab),
-    OPEN_IN_INCOGNITO_TAB(R.string.contextmenu_open_in_incognito_tab,
-            R.id.contextmenu_open_in_incognito_tab),
-    COPY_LINK_ADDRESS(R.string.contextmenu_copy_link_address, R.id.contextmenu_copy_link_address),
-    COPY_LINK_TEXT(R.string.contextmenu_copy_link_text, R.id.contextmenu_copy_link_text),
-    SAVE_LINK_AS(R.string.contextmenu_save_link, R.id.contextmenu_save_link_as),
+    OPEN_IN_NEW_TAB(R.drawable.context_menu_new_tab, R.string.contextmenu_open_in_new_tab,
+            R.id.contextmenu_open_in_new_tab),
+    OPEN_IN_INCOGNITO_TAB(R.drawable.incognito_statusbar,
+            R.string.contextmenu_open_in_incognito_tab, R.id.contextmenu_open_in_incognito_tab),
+    COPY_LINK_ADDRESS(R.drawable.ic_content_copy, R.string.contextmenu_copy_link_address,
+            R.id.contextmenu_copy_link_address),
+    COPY_LINK_TEXT(R.drawable.ic_content_copy, R.string.contextmenu_copy_link_text,
+            R.id.contextmenu_copy_link_text),
+    SAVE_LINK_AS(R.drawable.ic_file_download_white_24dp, R.string.contextmenu_save_link,
+            R.id.contextmenu_save_link_as),
 
     // Image Group
-    LOAD_ORIGINAL_IMAGE(R.string.contextmenu_load_original_image,
-            R.id.contextmenu_load_original_image),
-    SAVE_IMAGE(R.string.contextmenu_save_image, R.id.contextmenu_save_image),
-    OPEN_IMAGE(R.string.contextmenu_open_image, R.id.contextmenu_open_image),
-    OPEN_IMAGE_IN_NEW_TAB(R.string.contextmenu_open_image_in_new_tab,
-            R.id.contextmenu_open_image_in_new_tab),
-    SEARCH_BY_IMAGE(R.string.contextmenu_search_web_for_image, R.id.contextmenu_search_by_image),
-    SHARE_IMAGE(R.string.contextmenu_share_image, R.id.contextmenu_share_image),
+    LOAD_ORIGINAL_IMAGE(R.drawable.context_menu_load_image,
+            R.string.contextmenu_load_original_image, R.id.contextmenu_load_original_image),
+    SAVE_IMAGE(R.drawable.ic_file_download_white_24dp, R.string.contextmenu_save_image,
+            R.id.contextmenu_save_image),
+    OPEN_IMAGE(R.drawable.context_menu_new_tab, R.string.contextmenu_open_image,
+            R.id.contextmenu_open_image),
+    OPEN_IMAGE_IN_NEW_TAB(R.drawable.context_menu_new_tab,
+            R.string.contextmenu_open_image_in_new_tab, R.id.contextmenu_open_image_in_new_tab),
+    SEARCH_BY_IMAGE(R.drawable.googleg, R.string.contextmenu_search_web_for_image,
+            R.id.contextmenu_search_by_image),
+    SHARE_IMAGE(R.drawable.ic_share_white_24dp, R.string.contextmenu_share_image,
+            R.id.contextmenu_share_image),
 
     // Message Group
-    CALL(R.string.contextmenu_call, R.id.contextmenu_call),
-    SEND_MESSAGE(R.string.contextmenu_send_message, R.id.contextmenu_send_message),
-    ADD_TO_CONTACTS(R.string.contextmenu_add_to_contacts, R.id.contextmenu_add_to_contacts),
-    COPY(R.string.contextmenu_copy, R.id.contextmenu_copy),
+    CALL(R.drawable.ic_phone_googblue_36dp, R.string.contextmenu_call, R.id.contextmenu_call),
+    SEND_MESSAGE(R.drawable.ic_email_googblue_36dp, R.string.contextmenu_send_message,
+            R.id.contextmenu_send_message),
+    ADD_TO_CONTACTS(R.drawable.context_menu_add_to_contacts, R.string.contextmenu_add_to_contacts,
+            R.id.contextmenu_add_to_contacts),
+    COPY(R.drawable.ic_content_copy, R.string.contextmenu_copy, R.id.contextmenu_copy),
 
     // Video Group
-    SAVE_VIDEO(R.string.contextmenu_save_video, R.id.contextmenu_save_video),
-
+    SAVE_VIDEO(R.drawable.ic_file_download_white_24dp, R.string.contextmenu_save_video,
+            R.id.contextmenu_save_video),
     // Other
-    OPEN_IN_CHROME(R.string.menu_open_in_chrome, R.id.menu_id_open_in_chrome);
+    OPEN_IN_CHROME(R.drawable.context_menu_new_tab, R.string.menu_open_in_chrome,
+            R.id.menu_id_open_in_chrome);
 
+    @DrawableRes public final int iconId;
     @StringRes public final int stringId;
     @IdRes public final int menuId;
 
     /**
      * A representation of a Context Menu Item. Each item should have a string and an id associated
      * with it.
+     * @param iconId The icon that appears in {@link TabularContextMenuUi} to represent each item.
      * @param stringId The string that describes the action of the item.
-     * @param menuId The id found in ids.xml
+     * @param menuId The id found in ids.xml.
      */
-    ContextMenuItem(@StringRes int stringId, @IdRes int menuId) {
+    ContextMenuItem(@DrawableRes int iconId, @StringRes int stringId, @IdRes int menuId) {
+        this.iconId = iconId;
         this.stringId = stringId;
         this.menuId = menuId;
     }
@@ -89,4 +108,22 @@
 
         return context.getString(stringId);
     }
+
+    /**
+     * Returns the drawable and the content description associated with the context menu. If no
+     * drawable is associated with the icon, null is returned for the drawable and the
+     * iconDescription.
+     */
+    Drawable getDrawableAndDescription(Context context) {
+        if (iconId == R.drawable.context_menu_new_tab
+                || iconId == R.drawable.context_menu_add_to_contacts
+                || iconId == R.drawable.context_menu_load_image) {
+            return ApiCompatibilityUtils.getDrawable(context.getResources(), iconId);
+        } else if (iconId == 0) {
+            return null;
+        } else {
+            return TintedDrawable.constructTintedDrawable(
+                    context.getResources(), iconId, R.color.light_normal_color);
+        }
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuParams.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuParams.java
index 963e43d..58ae91d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuParams.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuParams.java
@@ -6,6 +6,7 @@
 
 import android.text.TextUtils;
 
+import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.UrlConstants;
@@ -133,7 +134,8 @@
         return false;
     }
 
-    private ContextMenuParams(int mediaType, String pageUrl, String linkUrl, String linkText,
+    @VisibleForTesting
+    ContextMenuParams(int mediaType, String pageUrl, String linkUrl, String linkText,
             String unfilteredLinkUrl, String srcUrl, String titleText, boolean imageWasFetchedLoFi,
             Referrer referrer, boolean canSavemedia) {
         mPageUrl = pageUrl;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java
index 5982197..8d23366 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.contextmenu;
 
-import android.app.Activity;
+import android.content.Context;
 import android.util.Pair;
 
 import org.chromium.base.Callback;
@@ -18,7 +18,7 @@
 public interface ContextMenuUi {
     /**
      * Shows the Context Menu in Chrome.
-     * @param activity Used to inflate the context menu.
+     * @param context Used to inflate the context menu.
      * @param params The current parameters for the the context menu.
      * @param items The list of items that need to be displayed in the context menu items. This is
      *              taken from the return value of {@link ContextMenuPopulator#buildContextMenu(
@@ -26,7 +26,12 @@
      * @param onItemClicked When the user has pressed an item the menuId associated with the item
      *                      is sent back through {@link Callback#onResult(Object)}. The ids that
      *                      could be called are in ids.xml.
+     * @param onMenuShown After the menu is displayed this method should be called to present a
+     *                    full menu.
+     * @param onMenuClosed When the menu is closed, this method is called to do any possible final
+     *                     clean up.
      */
-    void displayMenu(Activity activity, ContextMenuParams params,
-            List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer> onItemClicked);
+    void displayMenu(Context context, ContextMenuParams params,
+            List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer> onItemClicked,
+            Runnable onMenuShown, Runnable onMenuClosed);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/PlatformContextMenuUi.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/PlatformContextMenuUi.java
index 5069c044e..c73f44c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/PlatformContextMenuUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/PlatformContextMenuUi.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.contextmenu;
 
-import android.app.Activity;
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -27,13 +26,12 @@
     }
 
     @Override
-    public void displayMenu(Activity activity, ContextMenuParams params,
-            List<Pair<Integer, List<ContextMenuItem>>> itemGroups,
-            final Callback<Integer> listener) {
-
+    public void displayMenu(Context context, ContextMenuParams params,
+            List<Pair<Integer, List<ContextMenuItem>>> itemGroups, final Callback<Integer> listener,
+            Runnable onMenuShown, Runnable onMenuClosed) {
         String headerText = ChromeContextMenuPopulator.createHeaderText(params);
         if (!TextUtils.isEmpty(headerText)) {
-            setHeaderText(activity, mMenu, headerText);
+            setHeaderText(context, mMenu, headerText);
         }
 
         MenuItem.OnMenuItemClickListener menuListener = new MenuItem.OnMenuItemClickListener() {
@@ -47,7 +45,7 @@
             List<ContextMenuItem> group = itemGroups.get(groupIndex).second;
             for (int itemIndex = 0; itemIndex < group.size(); itemIndex++) {
                 ContextMenuItem item = group.get(itemIndex);
-                MenuItem menuItem = mMenu.add(0, item.menuId, 0, item.getString(activity));
+                MenuItem menuItem = mMenu.add(0, item.menuId, 0, item.getString(context));
                 menuItem.setOnMenuItemClickListener(menuListener);
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java
new file mode 100644
index 0000000..dbe631d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java
@@ -0,0 +1,83 @@
+// Copyright 2017 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.contextmenu;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+
+import java.util.List;
+
+/**
+ * Takes a list of {@link ContextMenuItem} and puts them in an adapter meant to be used within a
+ * list view.
+ */
+class TabularContextMenuListAdapter extends BaseAdapter {
+    private final List<ContextMenuItem> mMenuItems;
+    private final Context mContext;
+
+    /**
+     * Adapter for the tabular context menu UI
+     * @param menuItems The list of items to display in the view.
+     * @param context Used to inflate the layout.
+     */
+    TabularContextMenuListAdapter(List<ContextMenuItem> menuItems, Context context) {
+        mMenuItems = menuItems;
+        mContext = context;
+    }
+
+    @Override
+    public int getCount() {
+        return mMenuItems.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return mMenuItems.get(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return mMenuItems.get(position).menuId;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ContextMenuItem menuItem = mMenuItems.get(position);
+        ViewHolderItem viewHolder;
+
+        if (convertView == null) {
+            LayoutInflater inflater = LayoutInflater.from(mContext);
+            convertView = inflater.inflate(R.layout.tabular_context_menu_row, null);
+
+            viewHolder = new ViewHolderItem();
+            viewHolder.mIcon = (ImageView) convertView.findViewById(R.id.context_menu_icon);
+            viewHolder.mText = (TextView) convertView.findViewById(R.id.context_text);
+
+            convertView.setTag(viewHolder);
+        } else {
+            viewHolder = (ViewHolderItem) convertView.getTag();
+        }
+
+        viewHolder.mText.setText(menuItem.getString(mContext));
+        Drawable icon = menuItem.getDrawableAndDescription(mContext);
+        viewHolder.mIcon.setImageDrawable(icon);
+        viewHolder.mIcon.setVisibility(icon != null ? View.VISIBLE : View.INVISIBLE);
+
+        return convertView;
+    }
+
+    private static class ViewHolderItem {
+        ImageView mIcon;
+        TextView mText;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuPagerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuPagerAdapter.java
new file mode 100644
index 0000000..436eca1
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuPagerAdapter.java
@@ -0,0 +1,71 @@
+// Copyright 2017 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.contextmenu;
+
+import android.support.v4.text.TextUtilsCompat;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewCompat;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Takes a list of views and strings and creates a wrapper for the ViewPager and Tab adapter.
+ */
+class TabularContextMenuPagerAdapter extends PagerAdapter {
+    private final List<Pair<String, ViewGroup>> mViewList;
+    private final boolean mIsRightToLeft;
+
+    /**
+     * Used in combination of a TabLayout to create a multi view layout.
+     * @param views Thew views to use in the pager Adapter.
+     */
+    TabularContextMenuPagerAdapter(List<Pair<String, ViewGroup>> views) {
+        mViewList = views;
+        mIsRightToLeft = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault())
+                == ViewCompat.LAYOUT_DIRECTION_RTL;
+    }
+
+    // Addresses the RTL display bug: https://code.google.com/p/android/issues/detail?id=56831
+    private int adjustIndexForDirectionality(int index, int count) {
+        if (mIsRightToLeft) {
+            return count - 1 - index;
+        }
+        return index;
+    }
+
+    @Override
+    public Object instantiateItem(ViewGroup container, int position) {
+        position = adjustIndexForDirectionality(position, getCount());
+        ViewGroup layout = mViewList.get(position).second;
+        container.addView(layout);
+        return layout;
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object object) {
+        position = adjustIndexForDirectionality(position, getCount());
+        container.removeViewAt(position);
+    }
+
+    @Override
+    public int getCount() {
+        return mViewList.size();
+    }
+
+    @Override
+    public boolean isViewFromObject(View view, Object object) {
+        return view == object;
+    }
+
+    @Override
+    public CharSequence getPageTitle(int position) {
+        position = adjustIndexForDirectionality(position, getCount());
+        return mViewList.get(position).first;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java
new file mode 100644
index 0000000..387ffd6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java
@@ -0,0 +1,183 @@
+// Copyright 2017 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.contextmenu;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.support.design.widget.TabLayout;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AlertDialog;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Callback;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A custom dialog that separates each group into separate tabs. It uses a dialog instead.
+ */
+public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemClickListener {
+    private Dialog mDialog;
+    private Callback<Integer> mCallback;
+    private int mMenuItemHeight;
+
+    @Override
+    public void displayMenu(Context context, ContextMenuParams params,
+            List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer> onItemClicked,
+            final Runnable onMenuShown, final Runnable onMenuClosed) {
+        mCallback = onItemClicked;
+        mDialog = createDialog(context, params, items);
+        mDialog.getWindow().setBackgroundDrawable(ApiCompatibilityUtils.getDrawable(
+                context.getResources(), R.drawable.bg_find_toolbar_popup));
+
+        mDialog.setOnShowListener(new DialogInterface.OnShowListener() {
+            @Override
+            public void onShow(DialogInterface dialogInterface) {
+                onMenuShown.run();
+            }
+        });
+
+        mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+            @Override
+            public void onDismiss(DialogInterface dialogInterface) {
+                onMenuClosed.run();
+            }
+        });
+
+        mDialog.show();
+    }
+
+    /**
+     * Returns the fully complete dialog based off the params and the itemGroups.
+     * @param context Used to inflate the dialog.
+     * @param params Used to get the header title.
+     * @param itemGroups If there is more than one group it will create a paged view.
+     * @return Returns a final dialog that does not have a background can be displayed using
+     *         {@link AlertDialog#show()}.
+     */
+    private Dialog createDialog(Context context, ContextMenuParams params,
+            List<Pair<Integer, List<ContextMenuItem>>> itemGroups) {
+        Dialog dialog = new Dialog(context);
+        dialog.setContentView(createPagerView(context, params, itemGroups));
+        return dialog;
+    }
+
+    /**
+     * Creates the view of a context menu. Based off the Context Type, it'll adjust the list of
+     * items and display only the ones that'll be on that specific group.
+     * @param context Used to get the resources of an item.
+     * @param params used to create the header text.
+     * @param items A set of Items to display in a context menu. Filtered based off the type.
+     * @return Returns a filled LinearLayout with all the context menu items
+     */
+    @VisibleForTesting
+    ViewGroup createContextMenuPageUi(
+            Context context, ContextMenuParams params, List<ContextMenuItem> items, int maxCount) {
+        ViewGroup baseLayout = (ViewGroup) LayoutInflater.from(context).inflate(
+                R.layout.tabular_context_menu_page, null);
+        ListView listView = (ListView) baseLayout.findViewById(R.id.selectable_items);
+        displayHeaderIfVisibleItems(params, baseLayout);
+
+        // Set the list adapter and get the height to display it appropriately in a dialog.
+        TabularContextMenuListAdapter listAdapter =
+                new TabularContextMenuListAdapter(items, context);
+        ViewGroup.LayoutParams layoutParams = listView.getLayoutParams();
+        layoutParams.height = measureApproximateListViewHeight(listView, listAdapter, maxCount);
+        listView.setLayoutParams(layoutParams);
+        listView.setAdapter(listAdapter);
+        listView.setOnItemClickListener(this);
+
+        return baseLayout;
+    }
+
+    private void displayHeaderIfVisibleItems(ContextMenuParams params, ViewGroup baseLayout) {
+        String headerText = ChromeContextMenuPopulator.createHeaderText(params);
+        TextView headerTextView = (TextView) baseLayout.findViewById(R.id.context_header_text);
+        if (TextUtils.isEmpty(headerText)) {
+            headerTextView.setVisibility(View.GONE);
+            baseLayout.findViewById(R.id.context_divider).setVisibility(View.GONE);
+            return;
+        }
+        headerTextView.setVisibility(View.VISIBLE);
+        headerTextView.setText(headerText);
+    }
+
+    /**
+     * To save time measuring the height, this method gets an item if the height has not been
+     * previous measured and multiplies it by count of the total amount of items. It is fine if the
+     * height too small as the ListView will scroll through the other values.
+     * @param listView The ListView to measure the surrounding padding.
+     * @param listAdapter The adapter which contains the items within the list.
+     * @return Returns the combined height of the padding of the ListView and the approximate height
+     *         of the ListView based off the an item.
+     */
+    private int measureApproximateListViewHeight(
+            ListView listView, BaseAdapter listAdapter, int maxCount) {
+        int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
+        if (mMenuItemHeight == 0 && !listAdapter.isEmpty()) {
+            View view = listAdapter.getView(0, null, listView);
+            view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+            mMenuItemHeight = view.getMeasuredHeight();
+        }
+        return totalHeight + mMenuItemHeight * maxCount;
+    }
+
+    /**
+     * Creates a ViewPageAdapter based off the given list of views.
+     * @param context Used to inflate the new ViewPager
+     * @param params Used to get the header text.
+     * @param itemGroups The list of views to put into the ViewPager. The string is the title of the
+     *                   tab
+     * @return Returns a complete tabular context menu view.
+     */
+    @VisibleForTesting
+    View createPagerView(Context context, ContextMenuParams params,
+            List<Pair<Integer, List<ContextMenuItem>>> itemGroups) {
+        View view = LayoutInflater.from(context).inflate(R.layout.tabular_context_menu, null);
+
+        List<Pair<String, ViewGroup>> viewGroups = new ArrayList<>();
+        int maxCount = 0;
+        for (int i = 0; i < itemGroups.size(); i++) {
+            Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i);
+            maxCount = Math.max(maxCount, itemGroup.second.size());
+        }
+        for (int i = 0; i < itemGroups.size(); i++) {
+            Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i);
+            viewGroups.add(new Pair<>(context.getString(itemGroup.first),
+                    createContextMenuPageUi(context, params, itemGroup.second, maxCount)));
+        }
+        TabularContextMenuViewPager pager =
+                (TabularContextMenuViewPager) view.findViewById(R.id.custom_pager);
+        pager.setAdapter(new TabularContextMenuPagerAdapter(viewGroups));
+
+        TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout);
+        if (itemGroups.size() <= 1) {
+            tabLayout.setVisibility(View.GONE);
+        }
+        tabLayout.setupWithViewPager((ViewPager) view.findViewById(R.id.custom_pager));
+
+        return view;
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+        mDialog.dismiss();
+        mCallback.onResult((int) id);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuViewPager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuViewPager.java
new file mode 100644
index 0000000..640aabe
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuViewPager.java
@@ -0,0 +1,53 @@
+// Copyright 2017 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.contextmenu;
+
+import android.content.Context;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.View;
+
+import org.chromium.chrome.R;
+
+/**
+ * When there is more than one view for the context menu to display, it wraps the display in a view
+ * pager.
+ */
+public class TabularContextMenuViewPager extends ViewPager {
+    public TabularContextMenuViewPager(Context context) {
+        super(context);
+    }
+
+    public TabularContextMenuViewPager(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /**
+     * Used to show the full ViewPager dialog. Without this the dialog would have no height or
+     * width.
+     */
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int maxHeight = 0;
+        int tabHeight = 0;
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            child.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.UNSPECIFIED));
+            int measuredHeight = child.getMeasuredHeight();
+
+            // The ViewPager also considers the tab layout one of its children, and needs to be
+            // treated separately from getting the largest height.
+            if (child.getId() == R.id.tab_layout && child.getVisibility() != GONE) {
+                tabHeight = measuredHeight;
+            } else {
+                maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+            }
+        }
+        maxHeight += tabHeight;
+        heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index e9567b1..19f0196 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -222,6 +222,10 @@
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuPopulator.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuTitleView.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java",
+  "java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java",
+  "java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java",
+  "java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuPagerAdapter.java",
+  "java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuViewPager.java",
   "java/src/org/chromium/chrome/browser/contextmenu/PlatformContextMenuUi.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/BarOverlapTapSuppression.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchBlacklist.java",
@@ -1286,6 +1290,7 @@
   "javatests/src/org/chromium/chrome/browser/compositor/layouts/MockResourcesForLayout.java",
   "javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java",
   "javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java",
+  "javatests/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUiTest.java",
   "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java",
   "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicyTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUiTest.java
new file mode 100644
index 0000000..9722996
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUiTest.java
@@ -0,0 +1,142 @@
+// Copyright 2017 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.contextmenu;
+
+import android.support.design.widget.TabLayout;
+import android.support.test.filters.SmallTest;
+import android.util.Pair;
+import android.view.View;
+import android.widget.TextView;
+
+import org.chromium.base.CollectionUtil;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.test.ChromeActivityTestCaseBase;
+import org.chromium.content_public.common.Referrer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * A class to checkout the TabularContextMenuUi. This confirms the the UI represents items and
+ * groups.
+ */
+public class TabularContextMenuUiTest extends ChromeActivityTestCaseBase<ChromeActivity> {
+    public TabularContextMenuUiTest() {
+        super(ChromeActivity.class);
+    }
+
+    private static class MockMenuParams extends ContextMenuParams {
+        private String mUrl = "";
+
+        private MockMenuParams(int mediaType, String pageUrl, String linkUrl, String linkText,
+                String unfilteredLinkUrl, String srcUrl, String titleText,
+                boolean imageWasFetchedLoFi, Referrer referrer, boolean canSavemedia) {
+            super(mediaType, pageUrl, linkUrl, linkText, unfilteredLinkUrl, srcUrl, titleText,
+                    imageWasFetchedLoFi, referrer, canSavemedia);
+        }
+
+        private MockMenuParams(String url) {
+            this(0, "", "", "", "", "", "", false, null, true);
+            mUrl = url;
+        }
+
+        @Override
+        public String getLinkUrl() {
+            return mUrl;
+        }
+    }
+
+    @Override
+    public void startMainActivity() throws InterruptedException {
+        startMainActivityOnBlankPage();
+    }
+
+    @SmallTest
+    @Feature({"CustomContextMenu"})
+    public void testViewDisplaysSingleItemProperly() throws ExecutionException {
+        final TabularContextMenuUi dialog = new TabularContextMenuUi();
+
+        final List<Pair<Integer, List<ContextMenuItem>>> itemGroups = new ArrayList<>();
+        List<ContextMenuItem> item = CollectionUtil.newArrayList(ContextMenuItem.ADD_TO_CONTACTS,
+                ContextMenuItem.CALL, ContextMenuItem.COPY_LINK_ADDRESS);
+        itemGroups.add(new Pair<>(R.string.contextmenu_link_title, item));
+        final String url = "http://google.com";
+        View view = ThreadUtils.runOnUiThreadBlocking(new Callable<View>() {
+            @Override
+            public View call() {
+                return dialog.createPagerView(getActivity(), new MockMenuParams(url), itemGroups);
+            }
+        });
+
+        TabLayout layout = (TabLayout) view.findViewById(R.id.tab_layout);
+        assertEquals(layout.getVisibility(), View.GONE);
+    }
+
+    @SmallTest
+    @Feature({"CustomContextMenu"})
+    public void testViewDisplaysViewPagerForMultipleItems() throws ExecutionException {
+        final TabularContextMenuUi dialog = new TabularContextMenuUi();
+
+        final List<Pair<Integer, List<ContextMenuItem>>> itemGroups = new ArrayList<>();
+        List<ContextMenuItem> item = CollectionUtil.newArrayList(ContextMenuItem.ADD_TO_CONTACTS,
+                ContextMenuItem.CALL, ContextMenuItem.COPY_LINK_ADDRESS);
+        itemGroups.add(new Pair<>(R.string.contextmenu_link_title, item));
+        itemGroups.add(new Pair<>(R.string.contextmenu_link_title, item));
+        final String url = "http://google.com";
+        View view = ThreadUtils.runOnUiThreadBlocking(new Callable<View>() {
+            @Override
+            public View call() {
+                return dialog.createPagerView(getActivity(), new MockMenuParams(url), itemGroups);
+            }
+        });
+
+        TabLayout layout = (TabLayout) view.findViewById(R.id.tab_layout);
+        assertEquals(layout.getVisibility(), View.VISIBLE);
+    }
+
+    @SmallTest
+    @Feature({"CustomContextMenu"})
+    public void testURLIsShownOnContextMenu() throws ExecutionException {
+        final TabularContextMenuUi dialog = new TabularContextMenuUi();
+        final List<ContextMenuItem> item =
+                CollectionUtil.newArrayList(ContextMenuItem.ADD_TO_CONTACTS, ContextMenuItem.CALL,
+                        ContextMenuItem.COPY_LINK_ADDRESS);
+        final String expectedUrl = "http://google.com";
+        View view = ThreadUtils.runOnUiThreadBlocking(new Callable<View>() {
+            @Override
+            public View call() {
+                return dialog.createContextMenuPageUi(
+                        getActivity(), new MockMenuParams(expectedUrl), item, item.size());
+            }
+        });
+
+        TextView textView = (TextView) view.findViewById(R.id.context_header_text);
+        assertEquals(expectedUrl, String.valueOf(textView.getText()));
+    }
+
+    @SmallTest
+    @Feature({"CustomContextMenu"})
+    public void testHeaderIsNotShownWhenThereIsNoParams() throws ExecutionException {
+        final TabularContextMenuUi dialog = new TabularContextMenuUi();
+        final List<ContextMenuItem> item =
+                CollectionUtil.newArrayList(ContextMenuItem.ADD_TO_CONTACTS, ContextMenuItem.CALL,
+                        ContextMenuItem.COPY_LINK_ADDRESS);
+        View view = ThreadUtils.runOnUiThreadBlocking(new Callable<View>() {
+            @Override
+            public View call() {
+                return dialog.createContextMenuPageUi(
+                        getActivity(), new MockMenuParams(""), item, item.size());
+            }
+        });
+
+        assertEquals(view.findViewById(R.id.context_header_text).getVisibility(), View.GONE);
+        assertEquals(view.findViewById(R.id.context_divider).getVisibility(), View.GONE);
+    }
+}
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index c33cfb5b..e3d084e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2485,6 +2485,13 @@
                                     kDelayNavigationFeatureVariations,
                                     "DelayNavigation")},
 
+#if defined(OS_ANDROID)
+    {"enable-custom-context-menu",
+     flag_descriptions::kEnableCustomContextMenuName,
+     flag_descriptions::kEnableCustomContextMenuDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kCustomContextMenu)},
+#endif  // OS_ANDROID
+
     // NOTE: Adding new command-line switches requires adding corresponding
     // entries to enum "LoginCustomFlags" in histograms.xml. See note in
     // histograms.xml and don't forget to run AboutFlagsHistogramTest unit test.
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index de980216..2d9479f 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -51,6 +51,7 @@
     &kChromeHomeFeature,
     &kContextualSearchSingleActions,
     &kContextualSearchUrlActions,
+    &kCustomContextMenu,
     &kCustomFeedbackUi,
     &kImportantSitesInCBD,
     &kImprovedA2HS,
@@ -121,6 +122,9 @@
 const base::Feature kContextualSearchUrlActions{
     "ContextualSearchUrlActions", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kCustomContextMenu{"CustomContextMenu",
+                                       base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kCustomFeedbackUi{"CustomFeedbackUi",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 4027b4f..0f87ecdd 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -22,6 +22,7 @@
 extern const base::Feature kChromeHomeFeature;
 extern const base::Feature kContextualSearchSingleActions;
 extern const base::Feature kContextualSearchUrlActions;
+extern const base::Feature kCustomContextMenu;
 extern const base::Feature kCustomFeedbackUi;
 extern const base::Feature kDownloadAutoResumptionThrottling;
 extern const base::Feature kImportantSitesInCBD;
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 63877eb..3cd7892c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2740,6 +2740,16 @@
     "Enables additional keyboard shortcuts that are useful for debugging "
     "Ash.";
 
+#if defined(OS_ANDROID)
+
+const char kEnableCustomContextMenuName[] = "Enable custom context menu";
+
+const char kEnableCustomContextMenuDescription[] =
+    "Enables a new context menu when a link, image, or video is pressed within "
+    "Chrome.";
+
+#endif  // defined(OS_ANDROID)
+
 #if defined(OS_CHROMEOS)
 
 //  File Manager
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 9e97ec7..08656e2 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2959,6 +2959,16 @@
 // Description of the 'Debugging keyboard shortcuts' lab.
 extern const char kDebugShortcutsDescription[];
 
+#if defined(OS_ANDROID)
+
+// Name for the flag to enable the custom context menu.
+extern const char kEnableCustomContextMenuName[];
+
+// Description for the flag to enable the custom context menu.
+extern const char kEnableCustomContextMenuDescription[];
+
+#endif  // defined(OS_ANDROID)
+
 #if defined(OS_CHROMEOS)
 
 //  File Manager
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 027255a..a06a3251 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -100615,6 +100615,7 @@
   <int value="379428799" label="security-chip-animation"/>
   <int value="385969127" label="disable-win32k-lockdown"/>
   <int value="387178525" label="VideoFullscreenOrientationLock:enabled"/>
+  <int value="388996324" label="CustomContextMenu:disabled"/>
   <int value="400322063" label="ash-disable-screen-orientation-lock"/>
   <int value="401983950" label="enable-spdy4"/>
   <int value="402143634" label="enable-search-button-in-omnibox-always"/>
@@ -100687,6 +100688,7 @@
   <int value="646252875" label="ReadItLaterInMenu:enabled"/>
   <int value="646738320" label="disable-gesture-editing"/>
   <int value="650602639" label="enable-autofill-keyboard-accessory-view"/>
+  <int value="652561231" label="CustomContextMenu:enabled"/>
   <int value="683410401"
       label="enable-proximity-auth-bluetooth-low-energy-discovery"/>
   <int value="684806628" label="TranslateLanguageByULP:disabled"/>