[go: nahoru, domu]

mash: Add shelf context menu support.

Chrome serializes its shelf item context menus for Ash.
Ash displays the menus and reports invocations to Chrome.

Add an ash-side ShelfContextMenu class that supports:
-Local menu items to set shelf & wallpaper prefs/settings.
-Proxies Chrome's menu items via mojo structs/interfaces.
-Also used for wallpaper/desktop context menus (via RWC).

Add ash::mojom::ShelfItemDelegate::GetContextMenuItems, etc.:
-Extend ExecuteCommand, MenuItem, etc. to support context menus.
-Denote the command's origin (context/app menu) and display id.
-Allow 64-bit int command and radio-group ids; null images.
-Add ash::mojom::MenuItemType=ui::MenuModel::ItemType traits.

Add common ash::ShelfItemDelegate context menu handling:
-Subclasses override new virtual GetContextMenu() helper.
-GetContextMenuItems() stores the MenuModel for execution.
-ExecuteContextMenuCommand() handles context menu invocation.

Wire up chrome's launcher item subclass context menus.
Make minor launcher context menu class simplifications.
Add a ShelfWindowWatcher context menu item for closing.

Remove mus, ash_shell, test, and desktop context menu classes.
Remove ShellDelegate::CreateContextMenu and ash::ShelfAlignmentMenu.
Inline CanChangeShelfAlignment; move CanUserModifyShelfAutoHideBehavior.
Add unit tests, flip TestWallpaperDelegate::CanOpenSetWallpaperPage.

Bug: 640693, 753028
Test: No Chrome OS shelf/desktop context menu behavior changes.
Change-Id: I3afb93095b4042dca57d16844c5b05308f06a384
TBR: caitkp@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/571517
Commit-Queue: Michael Wasserman <msw@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#495735}
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index dfc809e..f641dac 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -287,8 +287,6 @@
     "shelf/overflow_button.h",
     "shelf/shelf.cc",
     "shelf/shelf.h",
-    "shelf/shelf_alignment_menu.cc",
-    "shelf/shelf_alignment_menu.h",
     "shelf/shelf_application_menu_model.cc",
     "shelf/shelf_application_menu_model.h",
     "shelf/shelf_background_animator.cc",
@@ -301,6 +299,8 @@
     "shelf/shelf_button_pressed_metric_tracker.cc",
     "shelf/shelf_button_pressed_metric_tracker.h",
     "shelf/shelf_constants.h",
+    "shelf/shelf_context_menu_model.cc",
+    "shelf/shelf_context_menu_model.h",
     "shelf/shelf_controller.cc",
     "shelf/shelf_controller.h",
     "shelf/shelf_layout_manager.cc",
@@ -861,7 +861,9 @@
     "//components/quirks",
     "//components/session_manager:base",
     "//components/signin/core/account_id",
+    "//components/strings",
     "//components/user_manager",
+    "//components/vector_icons",
     "//components/viz/service",
     "//components/wallpaper",
     "//device/bluetooth",
@@ -877,7 +879,6 @@
     "//skia",
 
     # TODO(msw): Remove this; only ash_with_content should depend on webkit.
-    "//components/vector_icons",
     "//third_party/WebKit/public:blink_headers",
     "//third_party/icu",
     "//third_party/qcms",
@@ -980,8 +981,6 @@
     "../ui/views/test/test_views_delegate_aura.cc",
     "shell/app_list.cc",
     "shell/bubble.cc",
-    "shell/context_menu.cc",
-    "shell/context_menu.h",
     "shell/example_app_list_presenter.cc",
     "shell/example_app_list_presenter.h",
     "shell/example_factory.h",
@@ -1183,6 +1182,7 @@
     "shelf/shelf_application_menu_model_unittest.cc",
     "shelf/shelf_background_animator_unittest.cc",
     "shelf/shelf_button_pressed_metric_tracker_unittest.cc",
+    "shelf/shelf_context_menu_model_unittest.cc",
     "shelf/shelf_controller_unittest.cc",
     "shelf/shelf_layout_manager_unittest.cc",
     "shelf/shelf_locking_manager_unittest.cc",
diff --git a/ash/DEPS b/ash/DEPS
index 4d63140..445c3b5 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -7,6 +7,7 @@
   "+components/quirks",
   "+components/session_manager",
   "+components/signin/core/account_id",
+  "+components/strings",
   "+components/ui_devtools",
   "+components/user_manager",
   "+components/vector_icons",
diff --git a/ash/mus/BUILD.gn b/ash/mus/BUILD.gn
index 6d78dc2..9d1ac5b 100644
--- a/ash/mus/BUILD.gn
+++ b/ash/mus/BUILD.gn
@@ -28,8 +28,6 @@
     "bridge/shell_port_mash.h",
     "bridge/workspace_event_handler_mus.cc",
     "bridge/workspace_event_handler_mus.h",
-    "context_menu_mus.cc",
-    "context_menu_mus.h",
     "disconnected_app_handler.cc",
     "disconnected_app_handler.h",
     "display_synchronizer.cc",
diff --git a/ash/mus/context_menu_mus.cc b/ash/mus/context_menu_mus.cc
deleted file mode 100644
index 0d81f48..0000000
--- a/ash/mus/context_menu_mus.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/mus/context_menu_mus.h"
-
-#include "ash/public/cpp/shelf_types.h"
-#include "ash/shelf/shelf.h"
-#include "ash/shell.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "ash/wallpaper/wallpaper_controller.h"
-#include "ash/wallpaper/wallpaper_delegate.h"
-
-namespace ash {
-
-ContextMenuMus::ContextMenuMus(Shelf* shelf)
-    : ui::SimpleMenuModel(nullptr), shelf_(shelf), alignment_menu_(shelf) {
-  set_delegate(this);
-  AddCheckItemWithStringId(MENU_AUTO_HIDE,
-                           IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE);
-  AddSubMenuWithStringId(MENU_ALIGNMENT_MENU,
-                         IDS_ASH_SHELF_CONTEXT_MENU_POSITION, &alignment_menu_);
-  AddItemWithStringId(MENU_CHANGE_WALLPAPER, IDS_AURA_SET_DESKTOP_WALLPAPER);
-}
-
-ContextMenuMus::~ContextMenuMus() {}
-
-bool ContextMenuMus::IsCommandIdChecked(int command_id) const {
-  if (command_id == MENU_AUTO_HIDE)
-    return shelf_->auto_hide_behavior() == SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
-  return false;
-}
-
-bool ContextMenuMus::IsCommandIdEnabled(int command_id) const {
-  if (command_id == MENU_CHANGE_WALLPAPER)
-    return Shell::Get()->wallpaper_delegate()->CanOpenSetWallpaperPage();
-  return true;
-}
-
-void ContextMenuMus::ExecuteCommand(int command_id, int event_flags) {
-  if (command_id == MENU_AUTO_HIDE) {
-    shelf_->SetAutoHideBehavior(shelf_->auto_hide_behavior() ==
-                                        SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
-                                    ? SHELF_AUTO_HIDE_BEHAVIOR_NEVER
-                                    : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
-  } else if (command_id == MENU_CHANGE_WALLPAPER) {
-    Shell::Get()->wallpaper_controller()->OpenSetWallpaperPage();
-  }
-}
-
-}  // namespace ash
diff --git a/ash/mus/context_menu_mus.h b/ash/mus/context_menu_mus.h
deleted file mode 100644
index f07f0ed..0000000
--- a/ash/mus/context_menu_mus.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_MUS_CONTEXT_MENU_MUS_H_
-#define ASH_MUS_CONTEXT_MENU_MUS_H_
-
-#include "ash/shelf/shelf_alignment_menu.h"
-#include "base/macros.h"
-#include "ui/base/models/simple_menu_model.h"
-
-namespace ash {
-
-class Shelf;
-
-// Context menu for mash.
-// TODO: Mimic logic in LauncherContextMenu. http://crbug.com/640693
-class ContextMenuMus : public ui::SimpleMenuModel,
-                       public ui::SimpleMenuModel::Delegate {
- public:
-  explicit ContextMenuMus(Shelf* shelf);
-  ~ContextMenuMus() override;
-
-  // ui::SimpleMenuModel::Delegate overrides:
-  bool IsCommandIdChecked(int command_id) const override;
-  bool IsCommandIdEnabled(int command_id) const override;
-  void ExecuteCommand(int command_id, int event_flags) override;
-
- private:
-  enum MenuItem {
-    MENU_AUTO_HIDE,
-    MENU_ALIGNMENT_MENU,
-    MENU_CHANGE_WALLPAPER,
-  };
-
-  Shelf* shelf_;
-  ShelfAlignmentMenu alignment_menu_;
-
-  DISALLOW_COPY_AND_ASSIGN(ContextMenuMus);
-};
-
-}  // namespace ash
-
-#endif  // ASH_MUS_CONTEXT_MENU_MUS_H_
diff --git a/ash/mus/shell_delegate_mus.cc b/ash/mus/shell_delegate_mus.cc
index 342cf895..ef0d867 100644
--- a/ash/mus/shell_delegate_mus.cc
+++ b/ash/mus/shell_delegate_mus.cc
@@ -8,7 +8,6 @@
 
 #include "ash/gpu_support_stub.h"
 #include "ash/mus/accessibility_delegate_mus.h"
-#include "ash/mus/context_menu_mus.h"
 #include "ash/mus/wallpaper_delegate_mus.h"
 #include "ash/palette_delegate.h"
 #include "base/memory/ptr_util.h"
@@ -105,11 +104,6 @@
   return nullptr;
 }
 
-ui::MenuModel* ShellDelegateMus::CreateContextMenu(Shelf* shelf,
-                                                   const ShelfItem* item) {
-  return new ContextMenuMus(shelf);
-}
-
 GPUSupport* ShellDelegateMus::CreateGPUSupport() {
   // TODO: http://crbug.com/647421.
   NOTIMPLEMENTED() << " Using a stub GPUSupport implementation";
diff --git a/ash/mus/shell_delegate_mus.h b/ash/mus/shell_delegate_mus.h
index fe79698..c5d11a7 100644
--- a/ash/mus/shell_delegate_mus.h
+++ b/ash/mus/shell_delegate_mus.h
@@ -39,8 +39,6 @@
   std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   std::unique_ptr<PaletteDelegate> CreatePaletteDelegate() override;
-  ui::MenuModel* CreateContextMenu(Shelf* shelf,
-                                   const ShelfItem* item) override;
   GPUSupport* CreateGPUSupport() override;
   base::string16 GetProductName() const override;
   gfx::Image GetDeprecatedAcceleratorImage() const override;
diff --git a/ash/public/cpp/remote_shelf_item_delegate.cc b/ash/public/cpp/remote_shelf_item_delegate.cc
index 9b6ff1d..9376c883 100644
--- a/ash/public/cpp/remote_shelf_item_delegate.cc
+++ b/ash/public/cpp/remote_shelf_item_delegate.cc
@@ -21,9 +21,18 @@
                           std::move(callback));
 }
 
-void RemoteShelfItemDelegate::ExecuteCommand(uint32_t command_id,
-                                             int32_t event_flags) {
-  delegate_->ExecuteCommand(command_id, event_flags);
+void RemoteShelfItemDelegate::GetContextMenuItems(
+    int64_t display_id,
+    GetContextMenuItemsCallback callback) {
+  delegate_->GetContextMenuItems(display_id, std::move(callback));
+}
+
+void RemoteShelfItemDelegate::ExecuteCommand(bool from_context_menu,
+                                             int64_t command_id,
+                                             int32_t event_flags,
+                                             int64_t display_id) {
+  delegate_->ExecuteCommand(from_context_menu, command_id, event_flags,
+                            display_id);
 }
 
 void RemoteShelfItemDelegate::Close() {
diff --git a/ash/public/cpp/remote_shelf_item_delegate.h b/ash/public/cpp/remote_shelf_item_delegate.h
index f9a9b8f..8c35997 100644
--- a/ash/public/cpp/remote_shelf_item_delegate.h
+++ b/ash/public/cpp/remote_shelf_item_delegate.h
@@ -22,7 +22,12 @@
                     int64_t display_id,
                     ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  void GetContextMenuItems(int64_t display_id,
+                           GetContextMenuItemsCallback callback) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
   void Close() override;
 
  private:
diff --git a/ash/public/cpp/shelf_item_delegate.cc b/ash/public/cpp/shelf_item_delegate.cc
index 7429933..9daf2b8 100644
--- a/ash/public/cpp/shelf_item_delegate.cc
+++ b/ash/public/cpp/shelf_item_delegate.cc
@@ -4,9 +4,38 @@
 
 #include "ash/public/cpp/shelf_item_delegate.h"
 
+#include "ui/base/models/menu_model.h"
+
 namespace ash {
 
-ShelfItemDelegate::ShelfItemDelegate(const ash::ShelfID& shelf_id)
+namespace {
+
+// Get a serialized list of mojo MenuItemPtr objects to transport a menu model.
+// NOTE: This does not support button items, some separator types, sublabels,
+// minor text, dynamic items, label fonts, accelerators, visibility, etc.
+MenuItemList GetMenuItemsForMojo(ui::MenuModel* model) {
+  MenuItemList items;
+  if (!model)
+    return items;
+  for (int i = 0; i < model->GetItemCount(); ++i) {
+    mojom::MenuItemPtr item(mojom::MenuItem::New());
+    DCHECK_NE(ui::MenuModel::TYPE_BUTTON_ITEM, model->GetTypeAt(i));
+    item->type = model->GetTypeAt(i);
+    item->command_id = model->GetCommandIdAt(i);
+    item->label = model->GetLabelAt(i);
+    item->checked = model->IsItemCheckedAt(i);
+    item->enabled = model->IsEnabledAt(i);
+    item->radio_group_id = model->GetGroupIdAt(i);
+    if (item->type == ui::MenuModel::TYPE_SUBMENU)
+      item->submenu = GetMenuItemsForMojo(model->GetSubmenuModelAt(i));
+    items.push_back(std::move(item));
+  }
+  return items;
+}
+
+}  // namespace
+
+ShelfItemDelegate::ShelfItemDelegate(const ShelfID& shelf_id)
     : shelf_id_(shelf_id), binding_(this), image_set_by_controller_(false) {}
 
 ShelfItemDelegate::~ShelfItemDelegate() {}
@@ -21,9 +50,35 @@
   return MenuItemList();
 }
 
+std::unique_ptr<ui::MenuModel> ShelfItemDelegate::GetContextMenu(
+    int64_t display_id) {
+  // Shelf items do not have any custom context menu entries by default.
+  return nullptr;
+}
+
 AppWindowLauncherItemController*
 ShelfItemDelegate::AsAppWindowLauncherItemController() {
   return nullptr;
 }
 
+bool ShelfItemDelegate::ExecuteContextMenuCommand(int64_t command_id,
+                                                  int32_t event_flags) {
+  DCHECK(context_menu_);
+  // Help subclasses execute context menu items, which may be on a sub-menu.
+  ui::MenuModel* model = context_menu_.get();
+  int index = -1;
+  if (!ui::MenuModel::GetModelAndIndexForCommandId(command_id, &model, &index))
+    return false;
+
+  model->ActivatedAt(index, event_flags);
+  return true;
+}
+
+void ShelfItemDelegate::GetContextMenuItems(
+    int64_t display_id,
+    GetContextMenuItemsCallback callback) {
+  context_menu_ = GetContextMenu(display_id);
+  std::move(callback).Run(GetMenuItemsForMojo(context_menu_.get()));
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/shelf_item_delegate.h b/ash/public/cpp/shelf_item_delegate.h
index 10562b1..688fd4b 100644
--- a/ash/public/cpp/shelf_item_delegate.h
+++ b/ash/public/cpp/shelf_item_delegate.h
@@ -17,6 +17,10 @@
 
 class AppWindowLauncherItemController;
 
+namespace ui {
+class MenuModel;
+}
+
 namespace ash {
 
 using MenuItemList = std::vector<mojom::MenuItemPtr>;
@@ -44,9 +48,19 @@
   // Returns items for the application menu; used for convenience and testing.
   virtual MenuItemList GetAppMenuItems(int event_flags);
 
+  // Returns the context menu model; used to show ShelfItem context menus.
+  virtual std::unique_ptr<ui::MenuModel> GetContextMenu(int64_t display_id);
+
   // Returns nullptr if class is not AppWindowLauncherItemController.
   virtual AppWindowLauncherItemController* AsAppWindowLauncherItemController();
 
+  // Attempts to execute a context menu command; returns true if it was run.
+  bool ExecuteContextMenuCommand(int64_t command_id, int32_t event_flags);
+
+  // mojom::ShelfItemDelegate:
+  void GetContextMenuItems(int64_t display_id,
+                           GetContextMenuItemsCallback callback) override;
+
  private:
   // The shelf id; empty if there is no app associated with the item.
   // Besides the application id, ShelfID also contains a launch id, which is an
@@ -61,6 +75,9 @@
   // Set to true if the launcher item image has been set by the controller.
   bool image_set_by_controller_;
 
+  // The context menu model that was last shown for the associated shelf item.
+  std::unique_ptr<ui::MenuModel> context_menu_;
+
   DISALLOW_COPY_AND_ASSIGN(ShelfItemDelegate);
 };
 
diff --git a/ash/public/cpp/shelf_prefs.cc b/ash/public/cpp/shelf_prefs.cc
index 30c8818..8e307e9 100644
--- a/ash/public/cpp/shelf_prefs.cc
+++ b/ash/public/cpp/shelf_prefs.cc
@@ -169,15 +169,6 @@
 
 }  // namespace
 
-bool CanUserModifyShelfAutoHideBehavior(PrefService* prefs) {
-  const std::string& pref = prefs::kShelfAutoHideBehaviorLocal;
-  auto* preference = prefs->FindPreference(pref);
-  if (!preference)
-    return true;
-
-  return preference->IsUserModifiable();
-}
-
 ShelfAutoHideBehavior GetShelfAutoHideBehaviorPref(PrefService* prefs,
                                                    int64_t display_id) {
   DCHECK_NE(display_id, display::kInvalidDisplayId);
diff --git a/ash/public/cpp/shelf_prefs.h b/ash/public/cpp/shelf_prefs.h
index 760519fb..714dbad 100644
--- a/ash/public/cpp/shelf_prefs.h
+++ b/ash/public/cpp/shelf_prefs.h
@@ -21,9 +21,6 @@
 ASH_PUBLIC_EXPORT extern const char kShelfAlignmentLeft[];
 ASH_PUBLIC_EXPORT extern const char kShelfAlignmentRight[];
 
-// Returns true if the user can modify the |shelf|'s auto-hide behavior.
-ASH_PUBLIC_EXPORT bool CanUserModifyShelfAutoHideBehavior(PrefService* prefs);
-
 // Get the shelf auto hide behavior preference for a particular display.
 ASH_PUBLIC_EXPORT ShelfAutoHideBehavior
 GetShelfAutoHideBehaviorPref(PrefService* prefs, int64_t display_id);
diff --git a/ash/public/cpp/shelf_struct_traits.h b/ash/public/cpp/shelf_struct_traits.h
index a5a112d..94911307 100644
--- a/ash/public/cpp/shelf_struct_traits.h
+++ b/ash/public/cpp/shelf_struct_traits.h
@@ -9,12 +9,59 @@
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/interfaces/shelf.mojom-shared.h"
+#include "ui/base/models/menu_model.h"
 
 using ash::ShelfItem;
 
 namespace mojo {
 
 template <>
+struct EnumTraits<ash::mojom::MenuItemType, ui::MenuModel::ItemType> {
+  static ash::mojom::MenuItemType ToMojom(ui::MenuModel::ItemType input) {
+    switch (input) {
+      case ui::MenuModel::TYPE_COMMAND:
+        return ash::mojom::MenuItemType::COMMAND;
+      case ui::MenuModel::TYPE_CHECK:
+        return ash::mojom::MenuItemType::CHECK;
+      case ui::MenuModel::TYPE_RADIO:
+        return ash::mojom::MenuItemType::RADIO;
+      case ui::MenuModel::TYPE_SEPARATOR:
+        return ash::mojom::MenuItemType::SEPARATOR;
+      case ui::MenuModel::TYPE_BUTTON_ITEM:
+        NOTREACHED() << "TYPE_BUTTON_ITEM is not yet supported.";
+        return ash::mojom::MenuItemType::COMMAND;
+      case ui::MenuModel::TYPE_SUBMENU:
+        return ash::mojom::MenuItemType::SUBMENU;
+    }
+    NOTREACHED();
+    return ash::mojom::MenuItemType::COMMAND;
+  }
+
+  static bool FromMojom(ash::mojom::MenuItemType input,
+                        ui::MenuModel::ItemType* out) {
+    switch (input) {
+      case ash::mojom::MenuItemType::COMMAND:
+        *out = ui::MenuModel::TYPE_COMMAND;
+        return true;
+      case ash::mojom::MenuItemType::CHECK:
+        *out = ui::MenuModel::TYPE_CHECK;
+        return true;
+      case ash::mojom::MenuItemType::RADIO:
+        *out = ui::MenuModel::TYPE_RADIO;
+        return true;
+      case ash::mojom::MenuItemType::SEPARATOR:
+        *out = ui::MenuModel::TYPE_SEPARATOR;
+        return true;
+      case ash::mojom::MenuItemType::SUBMENU:
+        *out = ui::MenuModel::TYPE_SUBMENU;
+        return true;
+    }
+    NOTREACHED();
+    return false;
+  }
+};
+
+template <>
 struct EnumTraits<ash::mojom::ShelfAction, ash::ShelfAction> {
   static ash::mojom::ShelfAction ToMojom(ash::ShelfAction input) {
     switch (input) {
diff --git a/ash/public/interfaces/shelf.mojom b/ash/public/interfaces/shelf.mojom
index be1b5e0..8f7c854 100644
--- a/ash/public/interfaces/shelf.mojom
+++ b/ash/public/interfaces/shelf.mojom
@@ -8,6 +8,16 @@
 import "skia/public/interfaces/bitmap.mojom";
 import "ui/events/mojo/event.mojom";
 
+// The types of menu items shown in shelf context and application list menus.
+// These values roughly match ui::MenuModel::ItemType (sans TYPE_BUTTON_ITEM).
+enum MenuItemType {
+  COMMAND,    // An item that performs an action when selected.
+  CHECK,      // An item that can be selected/checked to toggle a boolean state.
+  RADIO,      // An item that can be selected/checked among a group of choices.
+  SEPARATOR,  // An item that shows a horizontal line separator.
+  SUBMENU,    // An item that presents a submenu within another menu.
+};
+
 // The actions that may be performed when a shelf item is selected.
 // These values match ash::ShelfAction.
 enum ShelfAction {
@@ -96,14 +106,24 @@
   // The callback reports the action taken and any app menu items to show.
   //
   // NOTE: This codepath is not currently used for context menu triggering.
-  // TODO(msw): Remove |display_id| once panels are removed. crbug.com/691099
+  // TODO(crbug.com/691099): Remove |display_id| once panels are removed.
   ItemSelected(ui.mojom.Event event,
                int64 display_id,
                ShelfLaunchSource source) => (ShelfAction action,
                                              array<MenuItem>? menu_items);
 
+  // Called when spawning a shelf item context menu, returns custom menu items.
+  // TODO(mash): Clients should push context menu items to Ash's shelf model.
+  GetContextMenuItems(int64 display_id) => (array<MenuItem> items);
+
   // Called on invocation of a shelf item's context or application menu command.
-  ExecuteCommand(uint32 command_id, int32 event_flags);
+  // |from_context_menu| is true if the command came from a context menu, or
+  // false if the command came from an application menu. If the |display_id| is
+  // unknown or irrelevant, callers may pass |display::kInvalidDisplayId|.
+  ExecuteCommand(bool from_context_menu,
+                 int64 command_id,
+                 int32 event_flags,
+                 int64 display_id);
 
   // Closes all windows associated with this shelf item.
   Close();
@@ -111,18 +131,15 @@
 
 // MenuItems are used to populate application menus for shelf items.
 // Note: Some menus only support a subset of these item features (eg. no icons).
-// Note: These are not yet used for shelf item or ash shell context menus.
 struct MenuItem {
-  enum Type { ITEM, CHECK, RADIO, SEPARATOR, SUBMENU };
-
-  Type type;                         // The type of the menu item.
-  uint32 command_id;                 // The client's arbitrary item command id.
+  MenuItemType type;                 // The type of the menu item.
+  int64 command_id;                  // The client's arbitrary item command id.
   mojo.common.mojom.String16 label;  // The string label, may be empty.
-  skia.mojom.Bitmap image;           // The image icon, may be null.
+  skia.mojom.Bitmap? image;          // The image icon, may be null.
   array<MenuItem>? submenu;          // The optional nested submenu item list.
   bool enabled;                      // The enabled state.
   bool checked;                      // The checked state.
-  uint32 radio_group_id;             // The radio group id.
+  int64 radio_group_id;              // The radio group id.
 };
 
 // Identifier for shelf items and their windows.
diff --git a/ash/public/interfaces/shelf.typemap b/ash/public/interfaces/shelf.typemap
index e3bd00f..6aa61cf 100644
--- a/ash/public/interfaces/shelf.typemap
+++ b/ash/public/interfaces/shelf.typemap
@@ -6,6 +6,7 @@
 public_headers = [
   "//ash/public/cpp/shelf_item.h",
   "//ash/public/cpp/shelf_types.h",
+  "//ui/base/models/menu_model.h",
 ]
 traits_headers = [ "//ash/public/cpp/shelf_struct_traits.h" ]
 sources = [
@@ -14,8 +15,10 @@
 public_deps = [
   "//mojo/common:common_custom_types",
   "//skia/public/interfaces",
+  "//ui/base",
 ]
 type_mappings = [
+  "ash.mojom.MenuItemType=ui::MenuModel::ItemType",
   "ash.mojom.ShelfAction=ash::ShelfAction",
   "ash.mojom.ShelfAlignment=ash::ShelfAlignment",
   "ash.mojom.ShelfAutoHideBehavior=ash::ShelfAutoHideBehavior",
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index cae78b9..cff66e02 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -22,11 +22,11 @@
 #include "ash/screen_util.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_context_menu_model.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shelf/shelf_window_targeter.h"
 #include "ash/shell.h"
-#include "ash/shell_delegate.h"
 #include "ash/shell_port.h"
 #include "ash/system/status_area_layout_manager.h"
 #include "ash/system/status_area_widget.h"
@@ -642,11 +642,11 @@
 
 void RootWindowController::ShowContextMenu(const gfx::Point& location_in_screen,
                                            ui::MenuSourceType source_type) {
-  ShellDelegate* delegate = Shell::Get()->shell_delegate();
-  DCHECK(delegate);
-  menu_model_.reset(delegate->CreateContextMenu(shelf_.get(), nullptr));
-  if (!menu_model_)
-    return;
+  const int64_t display_id = display::Screen::GetScreen()
+                                 ->GetDisplayNearestWindow(GetRootWindow())
+                                 .id();
+  menu_model_ = base::MakeUnique<ShelfContextMenuModel>(
+      std::vector<mojom::MenuItemPtr>(), nullptr, display_id);
 
   menu_model_adapter_ = base::MakeUnique<views::MenuModelAdapter>(
       menu_model_.get(),
diff --git a/ash/shelf/app_list_shelf_item_delegate.cc b/ash/shelf/app_list_shelf_item_delegate.cc
index 62eb73e..00e5f063 100644
--- a/ash/shelf/app_list_shelf_item_delegate.cc
+++ b/ash/shelf/app_list_shelf_item_delegate.cc
@@ -25,9 +25,11 @@
   std::move(callback).Run(SHELF_ACTION_APP_LIST_SHOWN, base::nullopt);
 }
 
-void AppListShelfItemDelegate::ExecuteCommand(uint32_t command_id,
-                                              int32_t event_flags) {
-  // This delegate does not support showing an application menu.
+void AppListShelfItemDelegate::ExecuteCommand(bool from_context_menu,
+                                              int64_t command_id,
+                                              int32_t event_flags,
+                                              int64_t display_id) {
+  // This delegate does not show custom context or application menu items.
   NOTIMPLEMENTED();
 }
 
diff --git a/ash/shelf/app_list_shelf_item_delegate.h b/ash/shelf/app_list_shelf_item_delegate.h
index bb985cf..b143f3a 100644
--- a/ash/shelf/app_list_shelf_item_delegate.h
+++ b/ash/shelf/app_list_shelf_item_delegate.h
@@ -21,7 +21,10 @@
                     int64_t display_id,
                     ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
   void Close() override;
 
  private:
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index c68ea77..9ada51cf6 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -9,7 +9,6 @@
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
-#include "ash/session/session_controller.h"
 #include "ash/shelf/shelf_bezel_event_handler.h"
 #include "ash/shelf/shelf_controller.h"
 #include "ash/shelf/shelf_layout_manager.h"
@@ -76,34 +75,6 @@
   return RootWindowController::ForWindow(window)->shelf();
 }
 
-// static
-bool Shelf::CanChangeShelfAlignment() {
-  if (Shell::Get()->session_controller()->IsUserSupervised())
-    return false;
-
-  const LoginStatus login_status =
-      Shell::Get()->session_controller()->login_status();
-
-  switch (login_status) {
-    case LoginStatus::LOCKED:
-    // Shelf alignment changes can be requested while being locked, but will
-    // be applied upon unlock.
-    case LoginStatus::USER:
-    case LoginStatus::OWNER:
-      return true;
-    case LoginStatus::PUBLIC:
-    case LoginStatus::SUPERVISED:
-    case LoginStatus::GUEST:
-    case LoginStatus::KIOSK_APP:
-    case LoginStatus::ARC_KIOSK_APP:
-    case LoginStatus::NOT_LOGGED_IN:
-      return false;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
 void Shelf::CreateShelfWidget(aura::Window* root) {
   DCHECK(!shelf_widget_);
   aura::Window* shelf_container =
diff --git a/ash/shelf/shelf.h b/ash/shelf/shelf.h
index f7de0e5..9a5c4aa 100644
--- a/ash/shelf/shelf.h
+++ b/ash/shelf/shelf.h
@@ -49,10 +49,6 @@
   // widget may not exist, or the shelf may not be visible.
   static Shelf* ForWindow(aura::Window* window);
 
-  // Returns if shelf alignment options are enabled, and the user is able to
-  // adjust the alignment (eg. not allowed in guest and supervised user modes).
-  static bool CanChangeShelfAlignment();
-
   void CreateShelfWidget(aura::Window* root);
   void ShutdownShelfWidget();
   void DestroyShelfWidget();
diff --git a/ash/shelf/shelf_alignment_menu.h b/ash/shelf/shelf_alignment_menu.h
deleted file mode 100644
index ad5edcf..0000000
--- a/ash/shelf/shelf_alignment_menu.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef ASH_SHELF_SHELF_ALIGNMENT_MENU_H_
-#define ASH_SHELF_SHELF_ALIGNMENT_MENU_H_
-
-#include "ash/ash_export.h"
-#include "base/macros.h"
-#include "ui/base/models/simple_menu_model.h"
-
-namespace ash {
-
-class Shelf;
-
-// Submenu for choosing the alignment of the shelf.
-class ASH_EXPORT ShelfAlignmentMenu : public ui::SimpleMenuModel,
-                                      public ui::SimpleMenuModel::Delegate {
- public:
-  explicit ShelfAlignmentMenu(Shelf* shelf);
-  ~ShelfAlignmentMenu() override;
-
-  // ui::SimpleMenuModel::Delegate overrides:
-  bool IsCommandIdChecked(int command_id) const override;
-  bool IsCommandIdEnabled(int command_id) const override;
-  void ExecuteCommand(int command_id, int event_flags) override;
-
- private:
-  enum MenuItem {
-    // Offset so as not to interfere with other menus.
-    MENU_ALIGN_LEFT = 500,
-    MENU_ALIGN_RIGHT,
-    MENU_ALIGN_BOTTOM,
-  };
-
-  Shelf* shelf_;
-
-  DISALLOW_COPY_AND_ASSIGN(ShelfAlignmentMenu);
-};
-
-}  // namespace ash
-
-#endif  // ASH_SHELF_SHELF_ALIGNMENT_MENU_H_
diff --git a/ash/shelf/shelf_application_menu_model.cc b/ash/shelf/shelf_application_menu_model.cc
index d991f34..edc710c 100644
--- a/ash/shelf/shelf_application_menu_model.cc
+++ b/ash/shelf/shelf_application_menu_model.cc
@@ -11,6 +11,7 @@
 
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "base/metrics/histogram_macros.h"
+#include "ui/display/types/display_constants.h"
 #include "ui/gfx/image/image.h"
 
 namespace {
@@ -59,8 +60,11 @@
                                                int event_flags) {
   DCHECK(IsCommandIdEnabled(command_id));
   // Have the delegate execute its own custom command id for the given item.
-  if (delegate_)
-    delegate_->ExecuteCommand(items_[command_id]->command_id, event_flags);
+  if (delegate_) {
+    // The display hosting the menu is irrelevant, windows activate in-place.
+    delegate_->ExecuteCommand(false, items_[command_id]->command_id,
+                              event_flags, display::kInvalidDisplayId);
+  }
   RecordMenuItemSelectedMetrics(command_id, items_.size());
 }
 
diff --git a/ash/shelf/shelf_context_menu_model.cc b/ash/shelf/shelf_context_menu_model.cc
new file mode 100644
index 0000000..139bb25
--- /dev/null
+++ b/ash/shelf/shelf_context_menu_model.cc
@@ -0,0 +1,254 @@
+// 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.
+
+#include "ash/shelf/shelf_context_menu_model.h"
+
+#include <string>
+
+#include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_prefs.h"
+#include "ash/public/cpp/shelf_types.h"
+#include "ash/root_window_controller.h"
+#include "ash/session/session_controller.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/wallpaper/wallpaper_controller.h"
+#include "ash/wallpaper/wallpaper_delegate.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "base/metrics/user_metrics.h"
+#include "base/numerics/safe_conversions.h"
+#include "components/prefs/pref_service.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/image/image.h"
+
+using l10n_util::GetStringUTF16;
+using SubmenuList = std::vector<std::unique_ptr<ui::MenuModel>>;
+
+namespace ash {
+
+namespace {
+
+// Find a menu item by command id; returns a stub item if no match was found.
+const mojom::MenuItemPtr& GetItem(const MenuItemList& items, int command_id) {
+  const uint32_t id = base::checked_cast<uint32_t>(command_id);
+  static const mojom::MenuItemPtr item_not_found(mojom::MenuItem::New());
+  for (const mojom::MenuItemPtr& item : items) {
+    if (item->command_id == id)
+      return item;
+    if (item->type == ui::MenuModel::TYPE_SUBMENU &&
+        item->submenu.has_value()) {
+      const mojom::MenuItemPtr& submenu_item =
+          GetItem(item->submenu.value(), command_id);
+      if (submenu_item->command_id == id)
+        return submenu_item;
+    }
+  }
+  return item_not_found;
+}
+
+// A shelf context submenu model; used for shelf alignment.
+class ShelfContextSubMenuModel : public ui::SimpleMenuModel {
+ public:
+  ShelfContextSubMenuModel(Delegate* delegate,
+                           const MenuItemList& items,
+                           SubmenuList* submenus)
+      : ui::SimpleMenuModel(delegate) {
+    ShelfContextMenuModel::AddItems(this, delegate, items, submenus);
+  }
+  ~ShelfContextSubMenuModel() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShelfContextSubMenuModel);
+};
+
+// Returns true if the user can modify the shelf's auto-hide behavior pref.
+bool CanUserModifyShelfAutoHide(PrefService* prefs) {
+  return prefs && prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)
+                      ->IsUserModifiable();
+}
+
+// Returns true if the display is showing a fullscreen window.
+// NOTE: This duplicates the functionality of Chrome's IsFullScreenMode.
+bool IsFullScreenMode(int64_t display_id) {
+  auto* controller = Shell::GetRootWindowControllerWithDisplayId(display_id);
+  return controller && controller->GetWindowForFullscreenMode();
+}
+
+// Add the context menu items to change shelf auto-hide and alignment settings
+// and to change the wallpaper for the display with the given |display_id|.
+void AddLocalMenuItems(MenuItemList* menu, int64_t display_id) {
+  PrefService* prefs =
+      Shell::Get()->session_controller()->GetLastActiveUserPrefService();
+  if (!prefs)  // Null during startup.
+    return;
+
+  // In fullscreen, the shelf is either hidden or auto-hidden, depending on
+  // the type of fullscreen. Do not show the auto-hide menu item while in
+  // fullscreen because it is confusing when the preference appears not to
+  // apply.
+  if (CanUserModifyShelfAutoHide(prefs) && !IsFullScreenMode(display_id)) {
+    mojom::MenuItemPtr auto_hide(mojom::MenuItem::New());
+    auto_hide->type = ui::MenuModel::TYPE_CHECK;
+    auto_hide->command_id = ShelfContextMenuModel::MENU_AUTO_HIDE;
+    auto_hide->label = GetStringUTF16(IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE);
+    auto_hide->checked = GetShelfAutoHideBehaviorPref(prefs, display_id) ==
+                         SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
+    auto_hide->enabled = true;
+    menu->push_back(std::move(auto_hide));
+  }
+
+  // Only allow alignment and wallpaper modifications by the owner or user.
+  LoginStatus status = Shell::Get()->session_controller()->login_status();
+  if (status != LoginStatus::USER && status != LoginStatus::OWNER)
+    return;
+
+  const ShelfAlignment alignment = GetShelfAlignmentPref(prefs, display_id);
+  mojom::MenuItemPtr alignment_menu(mojom::MenuItem::New());
+  alignment_menu->type = ui::MenuModel::TYPE_SUBMENU;
+  alignment_menu->command_id = ShelfContextMenuModel::MENU_ALIGNMENT_MENU;
+  alignment_menu->label = GetStringUTF16(IDS_ASH_SHELF_CONTEXT_MENU_POSITION);
+  alignment_menu->submenu = MenuItemList();
+  alignment_menu->enabled = true;
+
+  mojom::MenuItemPtr left(mojom::MenuItem::New());
+  left->type = ui::MenuModel::TYPE_RADIO;
+  left->command_id = ShelfContextMenuModel::MENU_ALIGNMENT_LEFT;
+  left->label = GetStringUTF16(IDS_ASH_SHELF_CONTEXT_MENU_ALIGN_LEFT);
+  left->checked = alignment == SHELF_ALIGNMENT_LEFT;
+  left->enabled = true;
+  alignment_menu->submenu->push_back(std::move(left));
+
+  mojom::MenuItemPtr bottom(mojom::MenuItem::New());
+  bottom->type = ui::MenuModel::TYPE_RADIO;
+  bottom->command_id = ShelfContextMenuModel::MENU_ALIGNMENT_BOTTOM;
+  bottom->label = GetStringUTF16(IDS_ASH_SHELF_CONTEXT_MENU_ALIGN_BOTTOM);
+  bottom->checked = alignment == SHELF_ALIGNMENT_BOTTOM ||
+                    alignment == SHELF_ALIGNMENT_BOTTOM_LOCKED;
+  bottom->enabled = true;
+  alignment_menu->submenu->push_back(std::move(bottom));
+
+  mojom::MenuItemPtr right(mojom::MenuItem::New());
+  right->type = ui::MenuModel::TYPE_RADIO;
+  right->command_id = ShelfContextMenuModel::MENU_ALIGNMENT_RIGHT;
+  right->label = GetStringUTF16(IDS_ASH_SHELF_CONTEXT_MENU_ALIGN_RIGHT);
+  right->checked = alignment == SHELF_ALIGNMENT_RIGHT;
+  right->enabled = true;
+  alignment_menu->submenu->push_back(std::move(right));
+
+  menu->push_back(std::move(alignment_menu));
+
+  if (Shell::Get()->wallpaper_delegate()->CanOpenSetWallpaperPage()) {
+    mojom::MenuItemPtr wallpaper(mojom::MenuItem::New());
+    wallpaper->command_id = ShelfContextMenuModel::MENU_CHANGE_WALLPAPER;
+    wallpaper->label = GetStringUTF16(IDS_AURA_SET_DESKTOP_WALLPAPER);
+    wallpaper->enabled = true;
+    menu->push_back(std::move(wallpaper));
+  }
+}
+
+}  // namespace
+
+ShelfContextMenuModel::ShelfContextMenuModel(MenuItemList menu_items,
+                                             ShelfItemDelegate* delegate,
+                                             int64_t display_id)
+    : ui::SimpleMenuModel(this),
+      menu_items_(std::move(menu_items)),
+      delegate_(delegate),
+      display_id_(display_id) {
+  // Append some menu items that are handled locally by Ash.
+  AddLocalMenuItems(&menu_items_, display_id);
+  AddItems(this, this, menu_items_, &submenus_);
+}
+
+ShelfContextMenuModel::~ShelfContextMenuModel() {}
+
+// static
+void ShelfContextMenuModel::AddItems(ui::SimpleMenuModel* model,
+                                     ui::SimpleMenuModel::Delegate* delegate,
+                                     const MenuItemList& items,
+                                     SubmenuList* submenus) {
+  for (const mojom::MenuItemPtr& item : items) {
+    switch (item->type) {
+      case ui::MenuModel::TYPE_COMMAND:
+        model->AddItem(item->command_id, item->label);
+        break;
+      case ui::MenuModel::TYPE_CHECK:
+        model->AddCheckItem(item->command_id, item->label);
+        break;
+      case ui::MenuModel::TYPE_RADIO:
+        model->AddRadioItem(item->command_id, item->label,
+                            item->radio_group_id);
+        break;
+      case ui::MenuModel::TYPE_SEPARATOR:
+        model->AddSeparator(ui::NORMAL_SEPARATOR);
+        break;
+      case ui::MenuModel::TYPE_BUTTON_ITEM:
+        NOTREACHED() << "TYPE_BUTTON_ITEM is not yet supported.";
+      case ui::MenuModel::TYPE_SUBMENU:
+        if (item->submenu.has_value()) {
+          std::unique_ptr<ui::MenuModel> submenu =
+              base::MakeUnique<ShelfContextSubMenuModel>(
+                  delegate, item->submenu.value(), submenus);
+          model->AddSubMenu(item->command_id, item->label, submenu.get());
+          submenus->push_back(std::move(submenu));
+        }
+        break;
+    }
+    if (!item->image.isNull()) {
+      model->SetIcon(model->GetIndexOfCommandId(item->command_id),
+                     gfx::Image::CreateFrom1xBitmap(item->image));
+    }
+  }
+}
+
+bool ShelfContextMenuModel::IsCommandIdChecked(int command_id) const {
+  return GetItem(menu_items_, command_id)->checked;
+}
+
+bool ShelfContextMenuModel::IsCommandIdEnabled(int command_id) const {
+  return GetItem(menu_items_, command_id)->enabled;
+}
+
+void ShelfContextMenuModel::ExecuteCommand(int command_id, int event_flags) {
+  DCHECK(IsCommandIdEnabled(command_id));
+  PrefService* prefs =
+      Shell::Get()->session_controller()->GetLastActiveUserPrefService();
+  if (!prefs)  // Null during startup.
+    return;
+
+  UserMetricsRecorder* metrics = Shell::Get()->metrics();
+  switch (command_id) {
+    case MENU_AUTO_HIDE:
+      SetShelfAutoHideBehaviorPref(
+          prefs, display_id_,
+          GetShelfAutoHideBehaviorPref(prefs, display_id_) ==
+                  SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
+              ? SHELF_AUTO_HIDE_BEHAVIOR_NEVER
+              : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
+      break;
+    case MENU_ALIGNMENT_LEFT:
+      metrics->RecordUserMetricsAction(UMA_SHELF_ALIGNMENT_SET_LEFT);
+      SetShelfAlignmentPref(prefs, display_id_, SHELF_ALIGNMENT_LEFT);
+      break;
+    case MENU_ALIGNMENT_RIGHT:
+      metrics->RecordUserMetricsAction(UMA_SHELF_ALIGNMENT_SET_RIGHT);
+      SetShelfAlignmentPref(prefs, display_id_, SHELF_ALIGNMENT_RIGHT);
+      break;
+    case MENU_ALIGNMENT_BOTTOM:
+      metrics->RecordUserMetricsAction(UMA_SHELF_ALIGNMENT_SET_BOTTOM);
+      SetShelfAlignmentPref(prefs, display_id_, SHELF_ALIGNMENT_BOTTOM);
+      break;
+    case MENU_CHANGE_WALLPAPER:
+      Shell::Get()->wallpaper_controller()->OpenSetWallpaperPage();
+      break;
+    default:
+      // Have the shelf item delegate execute the context menu command.
+      if (delegate_)
+        delegate_->ExecuteCommand(true, command_id, event_flags, display_id_);
+      break;
+  }
+}
+
+}  // namespace ash
diff --git a/ash/shelf/shelf_context_menu_model.h b/ash/shelf/shelf_context_menu_model.h
new file mode 100644
index 0000000..6f4494d
--- /dev/null
+++ b/ash/shelf/shelf_context_menu_model.h
@@ -0,0 +1,66 @@
+// 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.
+
+#ifndef ASH_SHELF_SHELF_CONTEXT_MENU_MODEL_H_
+#define ASH_SHELF_SHELF_CONTEXT_MENU_MODEL_H_
+
+#include <memory>
+#include <vector>
+
+#include "ash/ash_export.h"
+#include "ash/public/interfaces/shelf.mojom.h"
+#include "base/macros.h"
+#include "ui/base/models/simple_menu_model.h"
+
+namespace ash {
+
+class ShelfItemDelegate;
+
+// A context menu shown for shelf items, the shelf itself, or the desktop area.
+class ASH_EXPORT ShelfContextMenuModel : public ui::SimpleMenuModel,
+                                         public ui::SimpleMenuModel::Delegate {
+ public:
+  // The command ids for locally-handled shelf and wallpaper context menu items.
+  enum CommandId {
+    MENU_LOCAL_START = 500,  // Offset to avoid conflicts with other menus.
+    MENU_AUTO_HIDE = MENU_LOCAL_START,
+    MENU_ALIGNMENT_MENU,
+    MENU_ALIGNMENT_LEFT,
+    MENU_ALIGNMENT_RIGHT,
+    MENU_ALIGNMENT_BOTTOM,
+    MENU_CHANGE_WALLPAPER,
+    MENU_LOCAL_END
+  };
+
+  // Creates a context menu model with |menu_items| from the given |delegate|.
+  // This menu appends and handles additional shelf and wallpaper menu items.
+  ShelfContextMenuModel(std::vector<mojom::MenuItemPtr> menu_items,
+                        ShelfItemDelegate* delegate,
+                        int64_t display_id);
+  ~ShelfContextMenuModel() override;
+
+  // Add the given |items| to |model|, populating |submenus| as needed.
+  // This is defined as static to support use by the submodel helper class.
+  static void AddItems(ui::SimpleMenuModel* model,
+                       ui::SimpleMenuModel::Delegate* delegate,
+                       const std::vector<mojom::MenuItemPtr>& items,
+                       std::vector<std::unique_ptr<ui::MenuModel>>* submenus);
+
+  // ui::SimpleMenuModel::Delegate overrides:
+  bool IsCommandIdChecked(int command_id) const override;
+  bool IsCommandIdEnabled(int command_id) const override;
+  void ExecuteCommand(int command_id, int event_flags) override;
+
+ private:
+  std::vector<mojom::MenuItemPtr> menu_items_;
+  ShelfItemDelegate* delegate_;
+  std::vector<std::unique_ptr<ui::MenuModel>> submenus_;
+  const int64_t display_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShelfContextMenuModel);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SHELF_SHELF_CONTEXT_MENU_MODEL_H_
diff --git a/ash/shelf/shelf_context_menu_model_unittest.cc b/ash/shelf/shelf_context_menu_model_unittest.cc
new file mode 100644
index 0000000..d48d6af
--- /dev/null
+++ b/ash/shelf/shelf_context_menu_model_unittest.cc
@@ -0,0 +1,259 @@
+// 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.
+
+#include "ash/shelf/shelf_context_menu_model.h"
+
+#include "ash/public/cpp/config.h"
+#include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/session/test_session_controller_client.h"
+#include "ash/shelf/shelf.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/test_shell_delegate.h"
+#include "ash/wallpaper/wallpaper_controller.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chromeos/login/scoped_test_public_session_login_state.h"
+#include "ui/display/display.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+namespace {
+
+using CommandId = ShelfContextMenuModel::CommandId;
+using MenuItemList = std::vector<mojom::MenuItemPtr>;
+
+class ShelfContextMenuModelTest : public AshTestBase {
+ public:
+  ShelfContextMenuModelTest() = default;
+  ~ShelfContextMenuModelTest() override = default;
+
+  void SetUp() override {
+    AshTestBase::SetUp();
+    TestSessionControllerClient* session = GetSessionControllerClient();
+    session->AddUserSession("user1@test.com");
+    session->SetSessionState(session_manager::SessionState::ACTIVE);
+    session->SwitchActiveUser(AccountId::FromUserEmail("user1@test.com"));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShelfContextMenuModelTest);
+};
+
+// A test wallpaper picker class that counts the number of times it is opened.
+class TestWallpaperPicker : public mojom::WallpaperPicker {
+ public:
+  TestWallpaperPicker() : binding_(this) {}
+  ~TestWallpaperPicker() override = default;
+
+  size_t open_count() const { return open_count_; }
+
+  mojom::WallpaperPickerPtr CreateInterfacePtr() {
+    mojom::WallpaperPickerPtr ptr;
+    binding_.Bind(mojo::MakeRequest(&ptr));
+    return ptr;
+  }
+
+  // mojom::WallpaperPicker:
+  void Open() override { open_count_++; }
+
+ private:
+  size_t open_count_ = 0;
+  mojo::Binding<mojom::WallpaperPicker> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWallpaperPicker);
+};
+
+// A test shelf item delegate that records the commands sent for execution.
+class TestShelfItemDelegate : public ShelfItemDelegate {
+ public:
+  TestShelfItemDelegate() : ShelfItemDelegate(ShelfID()) {}
+  ~TestShelfItemDelegate() override = default;
+
+  int last_executed_command() const { return last_executed_command_; }
+
+  // ShelfItemDelegate:
+  void ItemSelected(std::unique_ptr<ui::Event> event,
+                    int64_t display_id,
+                    ShelfLaunchSource source,
+                    ItemSelectedCallback callback) override {}
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override {
+    ASSERT_TRUE(from_context_menu);
+    last_executed_command_ = command_id;
+  }
+  void Close() override {}
+
+ private:
+  int last_executed_command_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TestShelfItemDelegate);
+};
+
+// Tests the default items in a shelf context menu.
+TEST_F(ShelfContextMenuModelTest, Basic) {
+  ShelfContextMenuModel menu(MenuItemList(), nullptr, GetPrimaryDisplay().id());
+
+  ASSERT_EQ(3, menu.GetItemCount());
+  EXPECT_EQ(CommandId::MENU_AUTO_HIDE, menu.GetCommandIdAt(0));
+  EXPECT_EQ(CommandId::MENU_ALIGNMENT_MENU, menu.GetCommandIdAt(1));
+  EXPECT_EQ(CommandId::MENU_CHANGE_WALLPAPER, menu.GetCommandIdAt(2));
+  for (int i = 0; i < menu.GetItemCount(); ++i) {
+    EXPECT_TRUE(menu.IsEnabledAt(i));
+    EXPECT_TRUE(menu.IsVisibleAt(i));
+  }
+
+  // Check the alignment submenu.
+  EXPECT_EQ(ui::MenuModel::TYPE_SUBMENU, menu.GetTypeAt(1));
+  ui::MenuModel* submenu = menu.GetSubmenuModelAt(1);
+  ASSERT_TRUE(submenu);
+  ASSERT_EQ(3, submenu->GetItemCount());
+  EXPECT_EQ(CommandId::MENU_ALIGNMENT_LEFT, submenu->GetCommandIdAt(0));
+  EXPECT_EQ(CommandId::MENU_ALIGNMENT_BOTTOM, submenu->GetCommandIdAt(1));
+  EXPECT_EQ(CommandId::MENU_ALIGNMENT_RIGHT, submenu->GetCommandIdAt(2));
+}
+
+// Test invocation of the default menu items.
+TEST_F(ShelfContextMenuModelTest, Invocation) {
+  int64_t primary_id = GetPrimaryDisplay().id();
+  Shelf* shelf = GetPrimaryShelf();
+
+  // Check the shelf auto-hide behavior and menu interaction.
+  ShelfContextMenuModel menu1(MenuItemList(), nullptr, primary_id);
+  EXPECT_FALSE(menu1.IsItemCheckedAt(0));
+  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
+  menu1.ActivatedAt(0);
+  EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
+
+  // This menu shows auto-hide enabled; check alignment and menu interaction.
+  ShelfContextMenuModel menu2(MenuItemList(), nullptr, primary_id);
+  EXPECT_TRUE(menu2.IsItemCheckedAt(0));
+  ui::MenuModel* submenu = menu2.GetSubmenuModelAt(1);
+  EXPECT_TRUE(submenu->IsItemCheckedAt(1));
+  EXPECT_EQ(SHELF_ALIGNMENT_BOTTOM, shelf->alignment());
+  submenu->ActivatedAt(0);
+  EXPECT_EQ(SHELF_ALIGNMENT_LEFT, shelf->alignment());
+
+  // This menu shows left alignment; check wallpaper item interaction.
+  ShelfContextMenuModel menu3(MenuItemList(), nullptr, primary_id);
+  submenu = menu3.GetSubmenuModelAt(1);
+  EXPECT_TRUE(submenu->IsItemCheckedAt(0));
+  TestWallpaperPicker picker;
+  Shell::Get()->wallpaper_controller()->SetWallpaperPicker(
+      picker.CreateInterfacePtr());
+  EXPECT_EQ(0u, picker.open_count());
+  menu3.ActivatedAt(2);
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(1u, picker.open_count());
+}
+
+// Tests the prepending of custom items in a shelf context menu.
+TEST_F(ShelfContextMenuModelTest, CustomItems) {
+  // Make a list of custom items with a variety of values.
+  MenuItemList items;
+  mojom::MenuItemPtr item(mojom::MenuItem::New());
+  item->type = ui::MenuModel::TYPE_COMMAND;
+  item->command_id = 123;
+  item->label = base::ASCIIToUTF16("item");
+  item->enabled = true;
+  items.push_back(std::move(item));
+  mojom::MenuItemPtr separator(mojom::MenuItem::New());
+  separator->type = ui::MenuModel::TYPE_SEPARATOR;
+  items.push_back(std::move(separator));
+  mojom::MenuItemPtr check(mojom::MenuItem::New());
+  check->type = ui::MenuModel::TYPE_CHECK;
+  check->command_id = 999;
+  check->label = base::ASCIIToUTF16("check");
+  check->enabled = true;
+  check->checked = false;
+  items.push_back(std::move(check));
+  mojom::MenuItemPtr radio(mojom::MenuItem::New());
+  radio->type = ui::MenuModel::TYPE_RADIO;
+  radio->command_id = 1337;
+  radio->label = base::ASCIIToUTF16("radio");
+  radio->enabled = false;
+  radio->checked = true;
+  items.push_back(std::move(radio));
+
+  // Ensure the menu model's prepended contents match the items above.
+  TestShelfItemDelegate delegate;
+  ShelfContextMenuModel menu(std::move(items), &delegate,
+                             GetPrimaryDisplay().id());
+  ASSERT_EQ(7, menu.GetItemCount());
+
+  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(0));
+  EXPECT_EQ(123, menu.GetCommandIdAt(0));
+  EXPECT_EQ(base::ASCIIToUTF16("item"), menu.GetLabelAt(0));
+  EXPECT_TRUE(menu.IsEnabledAt(0));
+
+  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(1));
+
+  EXPECT_EQ(ui::MenuModel::TYPE_CHECK, menu.GetTypeAt(2));
+  EXPECT_EQ(999, menu.GetCommandIdAt(2));
+  EXPECT_EQ(base::ASCIIToUTF16("check"), menu.GetLabelAt(2));
+  EXPECT_TRUE(menu.IsEnabledAt(2));
+  EXPECT_FALSE(menu.IsItemCheckedAt(2));
+
+  EXPECT_EQ(ui::MenuModel::TYPE_RADIO, menu.GetTypeAt(3));
+  EXPECT_EQ(1337, menu.GetCommandIdAt(3));
+  EXPECT_EQ(base::ASCIIToUTF16("radio"), menu.GetLabelAt(3));
+  EXPECT_FALSE(menu.IsEnabledAt(3));
+  EXPECT_TRUE(menu.IsItemCheckedAt(3));
+
+  // The default contents should appear at the bottom.
+  EXPECT_EQ(CommandId::MENU_AUTO_HIDE, menu.GetCommandIdAt(4));
+  EXPECT_EQ(CommandId::MENU_ALIGNMENT_MENU, menu.GetCommandIdAt(5));
+  EXPECT_EQ(CommandId::MENU_CHANGE_WALLPAPER, menu.GetCommandIdAt(6));
+
+  // Invoking a custom item should execute the command id on the delegate.
+  menu.ActivatedAt(2);
+  EXPECT_EQ(999, delegate.last_executed_command());
+}
+
+// Tests the prepending of a custom submenu in a shelf context menu.
+TEST_F(ShelfContextMenuModelTest, CustomSubmenu) {
+  // Make a list of custom items that includes a submenu.
+  MenuItemList items;
+  mojom::MenuItemPtr submenu_item(mojom::MenuItem::New());
+  submenu_item->type = ui::MenuModel::TYPE_SUBMENU;
+  MenuItemList submenu_items;
+  submenu_items.push_back(mojom::MenuItem::New());
+  submenu_items.push_back(mojom::MenuItem::New());
+  submenu_item->submenu = std::move(submenu_items);
+  items.push_back(std::move(submenu_item));
+
+  // Ensure the menu model's prepended contents match the items above.
+  ShelfContextMenuModel menu(std::move(items), nullptr,
+                             GetPrimaryDisplay().id());
+  ASSERT_EQ(4, menu.GetItemCount());
+  EXPECT_EQ(ui::MenuModel::TYPE_SUBMENU, menu.GetTypeAt(0));
+  ui::MenuModel* submenu = menu.GetSubmenuModelAt(0);
+  EXPECT_EQ(2, submenu->GetItemCount());
+
+  // The default contents should appear at the bottom.
+  EXPECT_EQ(CommandId::MENU_AUTO_HIDE, menu.GetCommandIdAt(1));
+  EXPECT_EQ(CommandId::MENU_ALIGNMENT_MENU, menu.GetCommandIdAt(2));
+  EXPECT_EQ(CommandId::MENU_CHANGE_WALLPAPER, menu.GetCommandIdAt(3));
+}
+
+// Tests fullscreen's per-display removal of "Autohide shelf": crbug.com/496681
+TEST_F(ShelfContextMenuModelTest, AutohideShelfOptionOnExternalDisplay) {
+  UpdateDisplay("940x550,940x550");
+  int64_t primary_id = GetPrimaryDisplay().id();
+  int64_t secondary_id = GetSecondaryDisplay().id();
+
+  // Create a normal window on the primary display.
+  std::unique_ptr<views::Widget> widget = CreateTestWidget();
+  widget->Show();
+  widget->SetFullscreen(true);
+
+  ShelfContextMenuModel primary_menu(MenuItemList(), nullptr, primary_id);
+  ShelfContextMenuModel secondary_menu(MenuItemList(), nullptr, secondary_id);
+  EXPECT_EQ(-1, primary_menu.GetIndexOfCommandId(CommandId::MENU_AUTO_HIDE));
+  EXPECT_NE(-1, secondary_menu.GetIndexOfCommandId(CommandId::MENU_AUTO_HIDE));
+}
+
+}  // namespace
+}  // namespace ash
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 24485b8..a275351a 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -23,6 +23,7 @@
 #include "ash/shelf/shelf_application_menu_model.h"
 #include "ash/shelf/shelf_button.h"
 #include "ash/shelf/shelf_constants.h"
+#include "ash/shelf/shelf_context_menu_model.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
@@ -189,6 +190,12 @@
   }
 }
 
+// Returns the id of the display on which |view| is shown.
+int64_t GetDisplayIdForView(View* view) {
+  aura::Window* window = view->GetWidget()->GetNativeWindow();
+  return display::Screen::GetScreen()->GetDisplayNearestWindow(window).id();
+}
+
 }  // namespace
 
 // AnimationDelegate used when deleting an item. This steadily decreased the
@@ -465,11 +472,9 @@
       break;
   }
 
-  const int64_t display_id =
-      display::Screen::GetScreen()->GetDisplayNearestWindow(window).id();
-
   // Notify the item of its selection; handle the result in AfterItemSelected.
   const ShelfItem& item = model_->items()[last_pressed_index_];
+  const int64_t display_id = GetDisplayIdForView(this);
 
   // Run AfterItemSelected directly if the item has no delegate (ie. in tests).
   if (!model_->GetShelfItemDelegate(item.id)) {
@@ -1696,7 +1701,7 @@
   if (action != SHELF_ACTION_APP_LIST_SHOWN) {
     if (action != SHELF_ACTION_NEW_WINDOW_CREATED && menu_items &&
         menu_items->size() > 1) {
-      // Show the app menu if there are 2 or more items and no window was shown.
+      // Show the app menu with 2 or more items, if no window was created.
       ink_drop->AnimateToState(views::InkDropState::ACTIVATED);
       context_menu_id_ = item.id;
       ShowMenu(base::MakeUnique<ShelfApplicationMenuModel>(
@@ -1713,13 +1718,28 @@
     scoped_root_window_for_new_windows_.reset();
 }
 
+void ShelfView::AfterGetContextMenuItems(
+    const ShelfID& shelf_id,
+    const gfx::Point& point,
+    ui::MenuSourceType source_type,
+    std::vector<mojom::MenuItemPtr> menu_items) {
+  context_menu_id_ = shelf_id;
+  const int64_t display_id = GetDisplayIdForView(this);
+  ShowMenu(base::MakeUnique<ShelfContextMenuModel>(
+               std::move(menu_items), model_->GetShelfItemDelegate(shelf_id),
+               display_id),
+           nullptr /* source */, point, true /* context_menu */, source_type,
+           nullptr /* ink_drop */);
+}
+
 void ShelfView::ShowContextMenuForView(views::View* source,
                                        const gfx::Point& point,
                                        ui::MenuSourceType source_type) {
   gfx::Point context_menu_point = point;
+  aura::Window* shelf_window = shelf_widget_->GetNativeWindow();
+
   // Align the context menu to the edge of the shelf for touch events.
   if (source_type == ui::MenuSourceType::MENU_SOURCE_TOUCH) {
-    aura::Window* shelf_window = shelf_widget_->GetNativeWindow();
     gfx::Rect shelf_bounds =
         is_overflow_mode()
             ? owner_overflow_bubble_->bubble_view()->GetBubbleBounds()
@@ -1742,20 +1762,21 @@
   }
   last_pressed_index_ = -1;
 
+  const int64_t display_id = GetDisplayIdForView(this);
   const ShelfItem* item = ShelfItemForView(source);
-  if (!item) {
-    ShellPort::Get()->ShowContextMenu(context_menu_point, source_type);
+  if (!item || !model_->GetShelfItemDelegate(item->id)) {
+    context_menu_id_ = ShelfID();
+    ShowMenu(base::MakeUnique<ShelfContextMenuModel>(
+                 std::vector<mojom::MenuItemPtr>(), nullptr, display_id),
+             source, context_menu_point, true, source_type, nullptr);
     return;
   }
 
-  std::unique_ptr<ui::MenuModel> context_menu_model(
-      Shell::Get()->shell_delegate()->CreateContextMenu(shelf_, item));
-  if (!context_menu_model)
-    return;
-
-  context_menu_id_ = item ? item->id : ShelfID();
-  ShowMenu(std::move(context_menu_model), source, context_menu_point, true,
-           source_type, nullptr);
+  // Get any custom entries; show the context menu in AfterGetContextMenuItems.
+  model_->GetShelfItemDelegate(item->id)->GetContextMenuItems(
+      display_id,
+      base::Bind(&ShelfView::AfterGetContextMenuItems,
+                 weak_factory_.GetWeakPtr(), item->id, point, source_type));
 }
 
 void ShelfView::ShowMenu(std::unique_ptr<ui::MenuModel> menu_model,
@@ -1778,7 +1799,7 @@
       new views::MenuRunner(menu_model_adapter_->CreateMenu(), run_types));
 
   // Place new windows on the same display as the button that spawned the menu.
-  aura::Window* window = source->GetWidget()->GetNativeWindow();
+  aura::Window* window = GetWidget()->GetNativeWindow();
   scoped_root_window_for_new_windows_.reset(
       new ScopedRootWindowForNewWindows(window->GetRootWindow()));
 
@@ -1786,6 +1807,7 @@
   gfx::Rect anchor = gfx::Rect(click_point, gfx::Size());
 
   if (!context_menu) {
+    DCHECK(source) << "Application lists require a source button view.";
     // Application lists use a bubble.
     // It is possible to invoke the menu while it is sliding into view. To cover
     // that case, the screen coordinates are offsetted by the animation delta.
@@ -1819,8 +1841,8 @@
   }
 
   // NOTE: if you convert to HAS_MNEMONICS be sure to update menu building code.
-  launcher_menu_runner_->RunMenuAt(source->GetWidget(), nullptr, anchor,
-                                   menu_alignment, source_type);
+  launcher_menu_runner_->RunMenuAt(GetWidget(), nullptr, anchor, menu_alignment,
+                                   source_type);
 }
 
 void ShelfView::OnMenuClosed(views::InkDrop* ink_drop) {
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index ce2d2499..d2e473d6 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -310,6 +310,13 @@
   void ShelfItemDelegateChanged(const ShelfID& id,
                                 ShelfItemDelegate* delegate) override;
 
+  // Handles the result when querying ShelfItemDelegates for context menu items.
+  // Shows a default shelf context menu with optional extra custom |menu_items|.
+  void AfterGetContextMenuItems(const ShelfID& shelf_id,
+                                const gfx::Point& point,
+                                ui::MenuSourceType source_type,
+                                std::vector<mojom::MenuItemPtr> menu_items);
+
   // Handles the result of an item selection, records the |action| taken and
   // optionally shows an application menu with the given |menu_items|.
   void AfterItemSelected(
diff --git a/ash/shelf/shelf_view_test_api.cc b/ash/shelf/shelf_view_test_api.cc
index 2784f44..543bcff 100644
--- a/ash/shelf/shelf_view_test_api.cc
+++ b/ash/shelf/shelf_view_test_api.cc
@@ -116,11 +116,12 @@
   shelf_view_->bounds_animator_->RemoveObserver(observer.get());
 }
 
-void ShelfViewTestAPI::CloseMenu() {
+bool ShelfViewTestAPI::CloseMenu() {
   if (!shelf_view_->launcher_menu_runner_)
-    return;
+    return false;
 
   shelf_view_->launcher_menu_runner_->Cancel();
+  return true;
 }
 
 OverflowBubble* ShelfViewTestAPI::overflow_bubble() {
diff --git a/ash/shelf/shelf_view_test_api.h b/ash/shelf/shelf_view_test_api.h
index 97b04798..97aab42 100644
--- a/ash/shelf/shelf_view_test_api.h
+++ b/ash/shelf/shelf_view_test_api.h
@@ -71,8 +71,8 @@
   // Runs message loop and waits until all add/remove animations are done.
   void RunMessageLoopUntilAnimationsDone();
 
-  // Closes the app list or context menu if it is running.
-  void CloseMenu();
+  // Close any open app list or context menu; returns true if a menu was closed.
+  bool CloseMenu();
 
   // An accessor for |shelf_view|.
   ShelfView* shelf_view() { return shelf_view_; }
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 6321445..593e6963 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/app_list_button.h"
 #include "ash/shelf/overflow_bubble.h"
@@ -52,7 +53,6 @@
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
-#include "ui/base/models/simple_menu_model.h"
 #include "ui/compositor/layer.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -166,7 +166,7 @@
     item_selected_count_++;
     std::move(callback).Run(item_selected_action_, base::nullopt);
   }
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override {}
+  void ExecuteCommand(bool, int64_t, int32_t, int64_t) override {}
   void Close() override {}
 
  private:
@@ -2003,6 +2003,39 @@
   EXPECT_EQ(animation_duration, observer.icon_positions_animation_duration());
 }
 
+// Tests that the blank shelf view area shows a context menu on right click.
+TEST_F(ShelfViewTest, ShelfViewShowsContextMenu) {
+  ui::test::EventGenerator& generator = GetEventGenerator();
+  generator.MoveMouseTo(shelf_view_->GetBoundsInScreen().CenterPoint());
+  generator.PressRightButton();
+  EXPECT_TRUE(test_api_->CloseMenu());
+}
+
+// Tests that the app list button shows a context menu on right click.
+TEST_F(ShelfViewTest, AppListButtonShowsContextMenu) {
+  ui::test::EventGenerator& generator = GetEventGenerator();
+  AppListButton* app_list_button = shelf_view_->GetAppListButton();
+  generator.MoveMouseTo(app_list_button->GetBoundsInScreen().CenterPoint());
+  generator.PressRightButton();
+  EXPECT_TRUE(test_api_->CloseMenu());
+}
+
+// Tests that ShelfWindowWatcher buttons show a context menu on right click.
+TEST_F(ShelfViewTest, ShelfWindowWatcherButtonShowsContextMenu) {
+  ui::test::EventGenerator& generator = GetEventGenerator();
+  std::unique_ptr<views::Widget> widget = CreateTestWidget();
+  widget->Show();
+  aura::Window* window = widget->GetNativeWindow();
+  ShelfID shelf_id(std::to_string(123));
+  window->SetProperty(kShelfIDKey, new std::string(shelf_id.Serialize()));
+  window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
+  ShelfButton* button = GetButtonByID(shelf_id);
+  ASSERT_TRUE(button);
+  generator.MoveMouseTo(button->GetBoundsInScreen().CenterPoint());
+  generator.PressRightButton();
+  EXPECT_TRUE(test_api_->CloseMenu());
+}
+
 class ShelfViewVisibleBoundsTest : public ShelfViewTest,
                                    public testing::WithParamInterface<bool> {
  public:
@@ -2128,7 +2161,7 @@
     items.push_back(mojom::MenuItem::New());
     std::move(callback).Run(SHELF_ACTION_NONE, std::move(items));
   }
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override {}
+  void ExecuteCommand(bool, int64_t, int32_t, int64_t) override {}
   void Close() override {}
 
   DISALLOW_COPY_AND_ASSIGN(ListMenuShelfItemDelegate);
@@ -2143,19 +2176,11 @@
   ~ShelfViewInkDropTest() override {}
 
   void SetUp() override {
-    ash_test_helper()->set_test_shell_delegate(CreateTestShellDelegate());
-
+    ash_test_helper()->set_test_shell_delegate(new TestShellDelegate());
     ShelfViewTest::SetUp();
   }
 
  protected:
-  // Gives subclasses a chance to return a custom test shell delegate to install
-  // before calling base class's SetUp(). Shell will take ownership of the
-  // returned object.
-  virtual TestShellDelegate* CreateTestShellDelegate() {
-    return new TestShellDelegate;
-  }
-
   void InitAppListButtonInkDrop() {
     app_list_button_ = shelf_view_->GetAppListButton();
 
@@ -2727,40 +2752,6 @@
 
 namespace {
 
-// An empty menu model for shell context menu just to have a menu.
-class TestShellMenuModel : public ui::SimpleMenuModel,
-                           public ui::SimpleMenuModel::Delegate {
- public:
-  TestShellMenuModel() : ui::SimpleMenuModel(this) {}
-  ~TestShellMenuModel() override {}
-
- private:
-  // ui::SimpleMenuModel::Delegate:
-  bool IsCommandIdChecked(int command_id) const override { return false; }
-  bool IsCommandIdEnabled(int command_id) const override { return true; }
-  void ExecuteCommand(int command_id, int event_flags) override {}
-
-  DISALLOW_COPY_AND_ASSIGN(TestShellMenuModel);
-};
-
-// A test ShellDelegate implementation for overflow button tests that returns a
-// TestShelfMenuModel for the shell context menu.
-class TestOverflowButtonShellDelegate : public TestShellDelegate {
- public:
-  TestOverflowButtonShellDelegate() {}
-  ~TestOverflowButtonShellDelegate() override {}
-
-  // TestShellDelegate:
-  ui::MenuModel* CreateContextMenu(Shelf* shelf,
-                                   const ShelfItem* item) override {
-    // Caller takes ownership of the returned object.
-    return new TestShellMenuModel;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestOverflowButtonShellDelegate);
-};
-
 std::string ToString(ShelfAlignment shelf_alignment) {
   switch (shelf_alignment) {
     case SHELF_ALIGNMENT_BOTTOM:
@@ -2810,11 +2801,6 @@
     return point;
   }
 
-  // Overridden from ShelfViewInkDropTest:
-  TestShellDelegate* CreateTestShellDelegate() override {
-    return new TestOverflowButtonShellDelegate;
-  }
-
   OverflowButton* overflow_button_ = nullptr;
   InkDropSpy* overflow_button_ink_drop_ = nullptr;
 
diff --git a/ash/shelf/shelf_window_watcher_item_delegate.cc b/ash/shelf/shelf_window_watcher_item_delegate.cc
index 17cac6b..fb591ee 100644
--- a/ash/shelf/shelf_window_watcher_item_delegate.cc
+++ b/ash/shelf/shelf_window_watcher_item_delegate.cc
@@ -8,12 +8,15 @@
 
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/window_properties.h"
+#include "ash/shelf/shelf_context_menu_model.h"
 #include "ash/shelf/shelf_controller.h"
 #include "ash/shell.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
+#include "components/strings/grit/components_strings.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/events/event_constants.h"
 #include "ui/wm/core/window_animations.h"
 
@@ -21,6 +24,9 @@
 
 namespace {
 
+// Close command id; avoids colliding with ShelfContextMenuModel command ids.
+const int kCloseCommandId = ShelfContextMenuModel::MENU_LOCAL_END + 1;
+
 ShelfItemType GetShelfItemType(const ShelfID& id) {
   ShelfModel* model = Shell::Get()->shelf_controller()->model();
   ShelfItems::const_iterator item = model->ItemByID(id);
@@ -67,8 +73,30 @@
   std::move(callback).Run(SHELF_ACTION_WINDOW_ACTIVATED, base::nullopt);
 }
 
-void ShelfWindowWatcherItemDelegate::ExecuteCommand(uint32_t command_id,
-                                                    int32_t event_flags) {}
+void ShelfWindowWatcherItemDelegate::GetContextMenuItems(
+    int64_t display_id,
+    GetContextMenuItemsCallback callback) {
+  // Show a default context menu with just an extra close item and separator.
+  ash::MenuItemList items;
+  ash::mojom::MenuItemPtr close(ash::mojom::MenuItem::New());
+  close->type = ui::MenuModel::TYPE_COMMAND;
+  close->command_id = kCloseCommandId;
+  close->label = l10n_util::GetStringUTF16(IDS_CLOSE);
+  close->enabled = true;
+  items.push_back(std::move(close));
+  ash::mojom::MenuItemPtr separator(ash::mojom::MenuItem::New());
+  separator->type = ui::MenuModel::TYPE_SEPARATOR;
+  items.push_back(std::move(separator));
+  std::move(callback).Run(std::move(items));
+}
+
+void ShelfWindowWatcherItemDelegate::ExecuteCommand(bool from_context_menu,
+                                                    int64_t command_id,
+                                                    int32_t event_flags,
+                                                    int64_t display_id) {
+  DCHECK_EQ(command_id, kCloseCommandId) << "Unknown ShelfItemDelegate command";
+  Close();
+}
 
 void ShelfWindowWatcherItemDelegate::Close() {
   wm::CloseWidgetForWindow(window_);
diff --git a/ash/shelf/shelf_window_watcher_item_delegate.h b/ash/shelf/shelf_window_watcher_item_delegate.h
index 00bbd6a..4f06c3b9 100644
--- a/ash/shelf/shelf_window_watcher_item_delegate.h
+++ b/ash/shelf/shelf_window_watcher_item_delegate.h
@@ -27,7 +27,12 @@
                     int64_t display_id,
                     ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  void GetContextMenuItems(int64_t display_id,
+                           GetContextMenuItemsCallback callback) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
   void Close() override;
 
   // The window associated with this item. Not owned.
diff --git a/ash/shell/context_menu.cc b/ash/shell/context_menu.cc
deleted file mode 100644
index f55a80a..0000000
--- a/ash/shell/context_menu.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/shell/context_menu.h"
-
-#include "ash/public/cpp/shelf_types.h"
-#include "ash/shelf/shelf.h"
-#include "ash/strings/grit/ash_strings.h"
-
-namespace ash {
-namespace shell {
-
-ContextMenu::ContextMenu(Shelf* shelf)
-    : ui::SimpleMenuModel(nullptr), shelf_(shelf), alignment_menu_(shelf) {
-  DCHECK(shelf_);
-  set_delegate(this);
-  AddCheckItemWithStringId(MENU_AUTO_HIDE,
-                           IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE);
-  AddSubMenuWithStringId(MENU_ALIGNMENT_MENU,
-                         IDS_ASH_SHELF_CONTEXT_MENU_POSITION, &alignment_menu_);
-}
-
-ContextMenu::~ContextMenu() {}
-
-bool ContextMenu::IsCommandIdChecked(int command_id) const {
-  if (command_id == MENU_AUTO_HIDE)
-    return shelf_->auto_hide_behavior() == SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
-  return false;
-}
-
-bool ContextMenu::IsCommandIdEnabled(int command_id) const {
-  return true;
-}
-
-void ContextMenu::ExecuteCommand(int command_id, int event_flags) {
-  if (command_id == MENU_AUTO_HIDE) {
-    shelf_->SetAutoHideBehavior(shelf_->auto_hide_behavior() ==
-                                        SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
-                                    ? SHELF_AUTO_HIDE_BEHAVIOR_NEVER
-                                    : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
-  }
-}
-
-}  // namespace shell
-}  // namespace ash
diff --git a/ash/shell/context_menu.h b/ash/shell/context_menu.h
deleted file mode 100644
index 547ce7c..0000000
--- a/ash/shell/context_menu.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef ASH_SHELL_CONTEXT_MENU_H_
-#define ASH_SHELL_CONTEXT_MENU_H_
-
-#include "ash/shelf/shelf_alignment_menu.h"
-#include "base/macros.h"
-#include "ui/base/models/simple_menu_model.h"
-
-namespace ash {
-
-class Shelf;
-
-namespace shell {
-
-// Context menu for the ash shell.
-class ContextMenu : public ui::SimpleMenuModel,
-                    public ui::SimpleMenuModel::Delegate {
- public:
-  explicit ContextMenu(Shelf* shelf);
-  ~ContextMenu() override;
-
-  // ui::SimpleMenuModel::Delegate overrides:
-  bool IsCommandIdChecked(int command_id) const override;
-  bool IsCommandIdEnabled(int command_id) const override;
-  void ExecuteCommand(int command_id, int event_flags) override;
-
- private:
-  enum MenuItem {
-    MENU_AUTO_HIDE,
-    MENU_ALIGNMENT_MENU,
-  };
-
-  Shelf* shelf_;
-  ShelfAlignmentMenu alignment_menu_;
-
-  DISALLOW_COPY_AND_ASSIGN(ContextMenu);
-};
-
-}  // namespace shell
-}  // namespace ash
-
-#endif  // ASH_SHELL_CONTEXT_MENU_H_
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index 3fb4074f..a6010b33 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -14,7 +14,6 @@
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
-#include "ash/shell/context_menu.h"
 #include "ash/shell/example_factory.h"
 #include "ash/shell/toplevel_window.h"
 #include "ash/wm/window_state.h"
@@ -128,11 +127,6 @@
   return base::MakeUnique<PaletteDelegateImpl>();
 }
 
-ui::MenuModel* ShellDelegateImpl::CreateContextMenu(Shelf* shelf,
-                                                    const ShelfItem* item) {
-  return new ContextMenu(shelf);
-}
-
 GPUSupport* ShellDelegateImpl::CreateGPUSupport() {
   // Real GPU support depends on src/content, so just use a stub.
   return new GPUSupportStub;
diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h
index a2751db..7b3c2d1 100644
--- a/ash/shell/shell_delegate_impl.h
+++ b/ash/shell/shell_delegate_impl.h
@@ -41,8 +41,6 @@
   std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   std::unique_ptr<PaletteDelegate> CreatePaletteDelegate() override;
-  ui::MenuModel* CreateContextMenu(Shelf* shelf,
-                                   const ShelfItem* item) override;
   GPUSupport* CreateGPUSupport() override;
   base::string16 GetProductName() const override;
   gfx::Image GetDeprecatedAcceleratorImage() const override;
diff --git a/ash/shell/window_watcher_shelf_item_delegate.cc b/ash/shell/window_watcher_shelf_item_delegate.cc
index bf30e39..52a7d84 100644
--- a/ash/shell/window_watcher_shelf_item_delegate.cc
+++ b/ash/shell/window_watcher_shelf_item_delegate.cc
@@ -36,9 +36,11 @@
   std::move(callback).Run(SHELF_ACTION_WINDOW_ACTIVATED, base::nullopt);
 }
 
-void WindowWatcherShelfItemDelegate::ExecuteCommand(uint32_t command_id,
-                                                    int32_t event_flags) {
-  // This delegate does not support showing an application menu.
+void WindowWatcherShelfItemDelegate::ExecuteCommand(bool from_context_menu,
+                                                    int64_t command_id,
+                                                    int32_t event_flags,
+                                                    int64_t display_id) {
+  // This delegate does not show custom context or application menu items.
   NOTIMPLEMENTED();
 }
 
diff --git a/ash/shell/window_watcher_shelf_item_delegate.h b/ash/shell/window_watcher_shelf_item_delegate.h
index da9499f..0edccc6 100644
--- a/ash/shell/window_watcher_shelf_item_delegate.h
+++ b/ash/shell/window_watcher_shelf_item_delegate.h
@@ -26,7 +26,10 @@
                     int64_t display_id,
                     ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
   void Close() override;
 
  private:
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index afdad31..3aae811 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -32,7 +32,6 @@
 
 namespace ui {
 class InputDeviceControllerClient;
-class MenuModel;
 }
 
 namespace ash {
@@ -41,8 +40,6 @@
 class GPUSupport;
 class NetworkingConfigDelegate;
 class PaletteDelegate;
-class Shelf;
-struct ShelfItem;
 class WallpaperDelegate;
 
 // Delegate of the Shell.
@@ -106,11 +103,6 @@
 
   virtual std::unique_ptr<PaletteDelegate> CreatePaletteDelegate() = 0;
 
-  // Creates a menu model for the |shelf| and optional shelf |item|.
-  // If |item| is null, this creates a context menu for the wallpaper or shelf.
-  virtual ui::MenuModel* CreateContextMenu(Shelf* shelf,
-                                           const ShelfItem* item) = 0;
-
   // Creates a GPU support object. Shell takes ownership of the object.
   virtual GPUSupport* CreateGPUSupport() = 0;
 
diff --git a/ash/shell_port.cc b/ash/shell_port.cc
index 8a0f81b..a264fa68 100644
--- a/ash/shell_port.cc
+++ b/ash/shell_port.cc
@@ -41,9 +41,10 @@
 
 void ShellPort::ShowContextMenu(const gfx::Point& location_in_screen,
                                 ui::MenuSourceType source_type) {
-  // Bail if there is no active user session or if the screen is locked.
+  // Bail with no active user session, in the lock screen, or in app/kiosk mode.
   if (Shell::Get()->session_controller()->NumberOfLoggedInUsers() < 1 ||
-      Shell::Get()->session_controller()->IsScreenLocked()) {
+      Shell::Get()->session_controller()->IsScreenLocked() ||
+      Shell::Get()->session_controller()->IsRunningInAppMode()) {
     return;
   }
 
diff --git a/ash/strings/BUILD.gn b/ash/strings/BUILD.gn
index b6c7fe1..a2341cc 100644
--- a/ash/strings/BUILD.gn
+++ b/ash/strings/BUILD.gn
@@ -77,6 +77,7 @@
     # Each input pak file should also have a deps line for completeness.
     sources = [
       "$root_gen_dir/ash/strings/ash_strings_${locale}.pak",
+      "$root_gen_dir/components/strings/components_strings_${locale}.pak",
       "$root_gen_dir/device/bluetooth/strings/bluetooth_strings_${locale}.pak",
       "$root_gen_dir/ui/chromeos/strings/ui_chromeos_strings_${locale}.pak",
       "$root_gen_dir/ui/strings/app_locale_settings_${locale}.pak",
@@ -85,6 +86,7 @@
 
     deps = [
       "//ash/strings",
+      "//components/strings",
       "//device/bluetooth/strings",
       "//ui/chromeos/strings",
       "//ui/strings:app_locale_settings",
diff --git a/ash/test_shell_delegate.cc b/ash/test_shell_delegate.cc
index 99eb46a..93483b5 100644
--- a/ash/test_shell_delegate.cc
+++ b/ash/test_shell_delegate.cc
@@ -116,11 +116,6 @@
   return nullptr;
 }
 
-ui::MenuModel* TestShellDelegate::CreateContextMenu(Shelf* shelf,
-                                                    const ShelfItem* item) {
-  return nullptr;
-}
-
 GPUSupport* TestShellDelegate::CreateGPUSupport() {
   // Real GPU support depends on src/content, so just use a stub.
   return new GPUSupportStub;
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h
index 1d32629f..9688ea34 100644
--- a/ash/test_shell_delegate.h
+++ b/ash/test_shell_delegate.h
@@ -46,8 +46,6 @@
   std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   std::unique_ptr<PaletteDelegate> CreatePaletteDelegate() override;
-  ui::MenuModel* CreateContextMenu(Shelf* shelf,
-                                   const ShelfItem* item) override;
   GPUSupport* CreateGPUSupport() override;
   base::string16 GetProductName() const override;
   gfx::Image GetDeprecatedAcceleratorImage() const override;
diff --git a/ash/wallpaper/test_wallpaper_delegate.cc b/ash/wallpaper/test_wallpaper_delegate.cc
index 5e3c689..f8b14bc 100644
--- a/ash/wallpaper/test_wallpaper_delegate.cc
+++ b/ash/wallpaper/test_wallpaper_delegate.cc
@@ -25,6 +25,10 @@
   update_wallpaper_count_++;
 }
 
+bool TestWallpaperDelegate::CanOpenSetWallpaperPage() {
+  return true;
+}
+
 int TestWallpaperDelegate::GetUpdateWallpaperCountAndReset() {
   int count = update_wallpaper_count_;
   update_wallpaper_count_ = 0;
diff --git a/ash/wallpaper/test_wallpaper_delegate.h b/ash/wallpaper/test_wallpaper_delegate.h
index 2edc6dcd..33e7dea 100644
--- a/ash/wallpaper/test_wallpaper_delegate.h
+++ b/ash/wallpaper/test_wallpaper_delegate.h
@@ -22,6 +22,7 @@
 
   // DefaultWallpaperDelegate overrides:
   void UpdateWallpaper(bool clear_cache) override;
+  bool CanOpenSetWallpaperPage() override;
 
   // Returns and clears |update_wallpaper_count_|.
   int GetUpdateWallpaperCountAndReset();
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 7ea350f..77f1d827 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1294,8 +1294,6 @@
       "ash/launcher/chrome_launcher_controller.h",
       "ash/launcher/chrome_launcher_controller_util.cc",
       "ash/launcher/chrome_launcher_controller_util.h",
-      "ash/launcher/desktop_shell_launcher_context_menu.cc",
-      "ash/launcher/desktop_shell_launcher_context_menu.h",
       "ash/launcher/extension_app_window_launcher_controller.cc",
       "ash/launcher/extension_app_window_launcher_controller.h",
       "ash/launcher/extension_app_window_launcher_item_controller.cc",
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index f6646b3..6bc26e8 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -47,7 +47,6 @@
 #include "chrome/browser/sync/sync_error_notifier_factory_ash.h"
 #include "chrome/browser/ui/ash/chrome_keyboard_ui.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
-#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/networking_config_delegate_chromeos.h"
 #include "chrome/browser/ui/ash/palette_delegate_chromeos.h"
@@ -504,23 +503,6 @@
   launcher_controller_.reset();
 }
 
-ui::MenuModel* ChromeShellDelegate::CreateContextMenu(
-    ash::Shelf* shelf,
-    const ash::ShelfItem* item) {
-  // Don't show context menu for exclusive app runtime mode.
-  if (chrome::IsRunningInAppMode())
-    return nullptr;
-
-  // No context menu before |launcher_controller_| is created. This is possible
-  // now because ShelfInit() is called by session state change via mojo
-  // asynchronously. Context menu could be triggered when the mojo message is
-  // still in-fly and crashes.
-  if (!launcher_controller_)
-    return nullptr;
-
-  return LauncherContextMenu::Create(launcher_controller_.get(), item, shelf);
-}
-
 ash::GPUSupport* ChromeShellDelegate::CreateGPUSupport() {
   // Chrome uses real GPU support.
   return new ash::GPUSupportImpl;
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index 705aff8..d7b96224 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -48,8 +48,6 @@
   std::unique_ptr<ash::WallpaperDelegate> CreateWallpaperDelegate() override;
   ash::AccessibilityDelegate* CreateAccessibilityDelegate() override;
   std::unique_ptr<ash::PaletteDelegate> CreatePaletteDelegate() override;
-  ui::MenuModel* CreateContextMenu(ash::Shelf* shelf,
-                                   const ash::ShelfItem* item) override;
   ash::GPUSupport* CreateGPUSupport() override;
   base::string16 GetProductName() const override;
   void OpenKeyboardShortcutHelpPage() const override;
diff --git a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc
index f454f2c..7710135 100644
--- a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc
@@ -144,8 +144,20 @@
   return items;
 }
 
-void AppShortcutLauncherItemController::ExecuteCommand(uint32_t command_id,
-                                                       int32_t event_flags) {
+std::unique_ptr<ui::MenuModel>
+AppShortcutLauncherItemController::GetContextMenu(int64_t display_id) {
+  ChromeLauncherController* controller = ChromeLauncherController::instance();
+  const ash::ShelfItem* item = controller->GetItem(shelf_id());
+  return LauncherContextMenu::Create(controller, item, display_id);
+}
+
+void AppShortcutLauncherItemController::ExecuteCommand(bool from_context_menu,
+                                                       int64_t command_id,
+                                                       int32_t event_flags,
+                                                       int64_t display_id) {
+  if (from_context_menu && ExecuteContextMenuCommand(command_id, event_flags))
+    return;
+
   if (static_cast<size_t>(command_id) >= app_menu_items_.size()) {
     app_menu_items_.clear();
     return;
diff --git a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h
index 40fb46d..87a5597 100644
--- a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h
@@ -42,7 +42,11 @@
                     ash::ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
   ash::MenuItemList GetAppMenuItems(int event_flags) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  std::unique_ptr<ui::MenuModel> GetContextMenu(int64_t display_id) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
   void Close() override;
 
   // Get the refocus url pattern, which can be used to identify this application
diff --git a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
index e5a924c6..73ff844 100644
--- a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
@@ -10,6 +10,7 @@
 #include "ash/wm/window_util.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
@@ -100,10 +101,11 @@
       action, GetAppMenuItems(event ? event->flags() : ui::EF_NONE));
 }
 
-void AppWindowLauncherItemController::ExecuteCommand(uint32_t command_id,
-                                                     int32_t event_flags) {
-  // This delegate does not support showing an application menu.
-  NOTIMPLEMENTED();
+std::unique_ptr<ui::MenuModel> AppWindowLauncherItemController::GetContextMenu(
+    int64_t display_id) {
+  ChromeLauncherController* controller = ChromeLauncherController::instance();
+  const ash::ShelfItem* item = controller->GetItem(shelf_id());
+  return LauncherContextMenu::Create(controller, item, display_id);
 }
 
 void AppWindowLauncherItemController::Close() {
diff --git a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
index 791a18a..12343f7 100644
--- a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
@@ -46,7 +46,7 @@
                     int64_t display_id,
                     ash::ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  std::unique_ptr<ui::MenuModel> GetContextMenu(int64_t display_id) override;
   void Close() override;
 
   // aura::WindowObserver overrides:
diff --git a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_item_controller.cc
index 0e212511..8f9d917 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_item_controller.cc
@@ -40,9 +40,12 @@
   std::move(callback).Run(ash::SHELF_ACTION_NONE, base::nullopt);
 }
 
-void ArcAppDeferredLauncherItemController::ExecuteCommand(uint32_t command_id,
-                                                          int32_t event_flags) {
-  // This delegate does not support showing an application menu.
+void ArcAppDeferredLauncherItemController::ExecuteCommand(
+    bool from_context_menu,
+    int64_t command_id,
+    int32_t event_flags,
+    int64_t display_id) {
+  // This delegate does not show custom context or application menu items.
   NOTIMPLEMENTED();
 }
 
diff --git a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_item_controller.h
index dba45ab..399778e 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_item_controller.h
@@ -35,7 +35,10 @@
                     int64_t display_id,
                     ash::ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
   void Close() override;
 
  private:
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
index d307b11..0143d418 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
@@ -6,14 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/ptr_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_icon_loader.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_window.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
 #include "ui/aura/window.h"
@@ -57,11 +50,6 @@
   std::move(callback).Run(ash::SHELF_ACTION_NEW_WINDOW_CREATED, base::nullopt);
 }
 
-void ArcAppWindowLauncherItemController::ExecuteCommand(uint32_t command_id,
-                                                        int32_t event_flags) {
-  ActivateIndexedApp(command_id);
-}
-
 ash::MenuItemList ArcAppWindowLauncherItemController::GetAppMenuItems(
     int event_flags) {
   ash::MenuItemList items;
@@ -80,3 +68,13 @@
 
   return items;
 }
+
+void ArcAppWindowLauncherItemController::ExecuteCommand(bool from_context_menu,
+                                                        int64_t command_id,
+                                                        int32_t event_flags,
+                                                        int64_t display_id) {
+  if (from_context_menu && ExecuteContextMenuCommand(command_id, event_flags))
+    return;
+
+  ActivateIndexedApp(command_id);
+}
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
index 3ea97b2..ab98786c 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
@@ -27,7 +27,10 @@
                     ash::ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
   ash::MenuItemList GetAppMenuItems(int event_flags) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
 
   void AddTaskId(int task_id);
   void RemoveTaskId(int task_id);
diff --git a/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc
index 41a44be..89915c3b 100644
--- a/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc
@@ -15,8 +15,8 @@
 ArcLauncherContextMenu::ArcLauncherContextMenu(
     ChromeLauncherController* controller,
     const ash::ShelfItem* item,
-    ash::Shelf* shelf)
-    : LauncherContextMenu(controller, item, shelf) {
+    int64_t display_id)
+    : LauncherContextMenu(controller, item, display_id) {
   Init();
 }
 
@@ -49,11 +49,4 @@
   if (app_is_open)
     AddItemWithStringId(MENU_CLOSE, IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
   AddSeparator(ui::NORMAL_SEPARATOR);
-  AddShelfOptionsMenu();
-}
-
-bool ArcLauncherContextMenu::IsCommandIdEnabled(int command_id) const {
-  if (command_id == MENU_OPEN_NEW)
-    return true;
-  return LauncherContextMenu::IsCommandIdEnabled(command_id);
 }
diff --git a/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.h b/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.h
index e23b3a4..58ea749 100644
--- a/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.h
+++ b/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.h
@@ -8,22 +8,14 @@
 #include "base/macros.h"
 #include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
 
-namespace ash {
-class Shelf;
-struct ShelfItem;
-}
-
 // Class for context menu which is shown for ARC app in the shelf.
 class ArcLauncherContextMenu : public LauncherContextMenu {
  public:
   ArcLauncherContextMenu(ChromeLauncherController* controller,
                          const ash::ShelfItem* item,
-                         ash::Shelf* shelf);
+                         int64_t display_id);
   ~ArcLauncherContextMenu() override;
 
-  // ui::SimpleMenuModel::Delegate overrides:
-  bool IsCommandIdEnabled(int command_id) const override;
-
  private:
   void Init();
 
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
index 957caecb..87b6297d 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/resources/grit/ash_resources.h"
+#include "ash/shelf/shelf_context_menu_model.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
@@ -263,9 +264,21 @@
   return items;
 }
 
+std::unique_ptr<ui::MenuModel>
+BrowserShortcutLauncherItemController::GetContextMenu(int64_t display_id) {
+  ChromeLauncherController* controller = ChromeLauncherController::instance();
+  const ash::ShelfItem* item = controller->GetItem(shelf_id());
+  return LauncherContextMenu::Create(controller, item, display_id);
+}
+
 void BrowserShortcutLauncherItemController::ExecuteCommand(
-    uint32_t command_id,
-    int32_t event_flags) {
+    bool from_context_menu,
+    int64_t command_id,
+    int32_t event_flags,
+    int64_t display_id) {
+  if (from_context_menu && ExecuteContextMenuCommand(command_id, event_flags))
+    return;
+
   const uint16_t browser_index = GetBrowserIndex(command_id);
   // Check that the index is valid and the browser has not been closed.
   if (browser_index < browser_menu_items_.size() &&
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h
index 0a9d4fc..24b5f40 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h
@@ -45,7 +45,11 @@
                     ash::ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
   ash::MenuItemList GetAppMenuItems(int event_flags) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  std::unique_ptr<ui::MenuModel> GetContextMenu(int64_t display_id) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
   void Close() override;
 
  private:
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 2bcc91c..1e191a0 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -349,16 +349,8 @@
     int index = model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT);
     DCHECK_GE(index, 0);
     ash::ShelfItem item = model_->items()[index];
-    Shelf* shelf = ash::Shelf::ForWindow(CurrentContext());
-    std::unique_ptr<LauncherContextMenu> menu(
-        LauncherContextMenu::Create(controller_, &item, shelf));
-    return menu;
-  }
-
-  aura::Window* CurrentContext() {
-    aura::Window* root_window = ash::Shell::GetRootWindowForNewWindows();
-    DCHECK(root_window);
-    return root_window;
+    int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+    return LauncherContextMenu::Create(controller_, &item, display_id);
   }
 
   bool IsItemPresentInMenu(LauncherContextMenu* menu, int command_id) {
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index f38b5f2..7e1dacac 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -270,7 +270,7 @@
                     ItemSelectedCallback callback) override {
     std::move(callback).Run(ash::SHELF_ACTION_WINDOW_ACTIVATED, base::nullopt);
   }
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override {}
+  void ExecuteCommand(bool, int64_t, int32_t, int64_t) override {}
   void Close() override {}
 
  private:
@@ -3444,7 +3444,8 @@
   {
     ash::MenuItemList items =
         launcher_controller_->GetAppMenuItemsForTesting(item_gmail);
-    item_delegate->ExecuteCommand(items[1]->command_id, ui::EF_NONE);
+    item_delegate->ExecuteCommand(false, items[1]->command_id, ui::EF_NONE,
+                                  display::kInvalidDisplayId);
     EXPECT_EQ(tabs, browser()->tab_strip_model()->count());
   }
 
@@ -3452,7 +3453,9 @@
   {
     ash::MenuItemList items =
         launcher_controller_->GetAppMenuItemsForTesting(item_gmail);
-    item_delegate->ExecuteCommand(items[1]->command_id, ui::EF_SHIFT_DOWN);
+    item_delegate->ExecuteCommand(false, items[1]->command_id,
+                                  ui::EF_SHIFT_DOWN,
+                                  display::kInvalidDisplayId);
     EXPECT_EQ(--tabs, browser()->tab_strip_model()->count());
   }
 }
@@ -3781,7 +3784,8 @@
   EXPECT_EQ(items[1]->command_id, 1U);
 
   // Execute command to activate first window.
-  item_delegate->ExecuteCommand(items[1]->command_id, 0);
+  item_delegate->ExecuteCommand(false, items[1]->command_id, ui::EF_NONE,
+                                display::kInvalidDisplayId);
   EXPECT_TRUE(window1->IsActive());
   EXPECT_FALSE(window2->IsActive());
 
@@ -3792,7 +3796,8 @@
   EXPECT_FALSE(window2->IsActive());
 
   // Execute command to activate second window.
-  item_delegate->ExecuteCommand(items[0]->command_id, 0);
+  item_delegate->ExecuteCommand(false, items[0]->command_id, ui::EF_NONE,
+                                display::kInvalidDisplayId);
   EXPECT_FALSE(window1->IsActive());
   EXPECT_TRUE(window2->IsActive());
 }
diff --git a/chrome/browser/ui/ash/launcher/desktop_shell_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/desktop_shell_launcher_context_menu.cc
deleted file mode 100644
index acd9e86..0000000
--- a/chrome/browser/ui/ash/launcher/desktop_shell_launcher_context_menu.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/ash/launcher/desktop_shell_launcher_context_menu.h"
-
-DesktopShellLauncherContextMenu::DesktopShellLauncherContextMenu(
-    ChromeLauncherController* controller,
-    const ash::ShelfItem* item,
-    ash::Shelf* shelf)
-    : LauncherContextMenu(controller, item, shelf) {
-  Init();
-}
-
-DesktopShellLauncherContextMenu::~DesktopShellLauncherContextMenu() {}
-
-void DesktopShellLauncherContextMenu::Init() {
-  AddShelfOptionsMenu();
-}
diff --git a/chrome/browser/ui/ash/launcher/desktop_shell_launcher_context_menu.h b/chrome/browser/ui/ash/launcher/desktop_shell_launcher_context_menu.h
deleted file mode 100644
index f6b6e85..0000000
--- a/chrome/browser/ui/ash/launcher/desktop_shell_launcher_context_menu.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_DESKTOP_SHELL_LAUNCHER_CONTEXT_MENU_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_DESKTOP_SHELL_LAUNCHER_CONTEXT_MENU_H_
-
-#include "base/macros.h"
-#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
-
-class ChromeLauncherController;
-
-namespace ash {
-class Shelf;
-struct ShelfItem;
-}
-
-// Class for context menu which is shown when right click on desktop shell.
-class DesktopShellLauncherContextMenu : public LauncherContextMenu {
- public:
-  DesktopShellLauncherContextMenu(ChromeLauncherController* controller,
-                                  const ash::ShelfItem* item,
-                                  ash::Shelf* shelf);
-  ~DesktopShellLauncherContextMenu() override;
-
- private:
-  void Init();
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopShellLauncherContextMenu);
-};
-
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_DESKTOP_SHELL_LAUNCHER_CONTEXT_MENU_H_
diff --git a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.cc
index 92b43ef..1916a98 100644
--- a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.cc
@@ -5,22 +5,15 @@
 #include "chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.h"
 
 #include "ash/public/cpp/shelf_item_delegate.h"
-#include "ash/wm/window_state.h"
-#include "ash/wm/window_util.h"
-#include "base/memory/ptr_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
-#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
 #include "components/favicon/content/content_favicon_driver.h"
-#include "content/public/browser/web_contents.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/browser/app_window/native_app_window.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
-#include "ui/events/event.h"
 #include "ui/gfx/image/image.h"
-#include "ui/wm/core/window_animations.h"
 
 ExtensionAppWindowLauncherItemController::
     ExtensionAppWindowLauncherItemController(const ash::ShelfID& shelf_id)
@@ -76,8 +69,13 @@
 }
 
 void ExtensionAppWindowLauncherItemController::ExecuteCommand(
-    uint32_t command_id,
-    int32_t event_flags) {
+    bool from_context_menu,
+    int64_t command_id,
+    int32_t event_flags,
+    int64_t display_id) {
+  if (from_context_menu && ExecuteContextMenuCommand(command_id, event_flags))
+    return;
+
   ChromeLauncherController::instance()->ActivateShellApp(app_id(), command_id);
 }
 
diff --git a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.h
index c14af96..ba134b3 100644
--- a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.h
@@ -30,7 +30,10 @@
 
   // AppWindowLauncherItemController:
   ash::MenuItemList GetAppMenuItems(int event_flags) override;
-  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ExtensionAppWindowLauncherItemController);
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
index e84bf2a..54b9b2bb 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
@@ -18,10 +18,10 @@
 #include "chrome/grit/generated_resources.h"
 #include "content/public/common/context_menu_params.h"
 #include "extensions/browser/extension_prefs.h"
-#include "ui/base/l10n/l10n_util.h"
 
 namespace {
 
+// A helper used to filter which menu items added by the extension are shown.
 bool MenuItemHasLauncherContext(const extensions::MenuItem* item) {
   return item->contexts().Contains(extensions::MenuItem::LAUNCHER);
 }
@@ -31,8 +31,8 @@
 ExtensionLauncherContextMenu::ExtensionLauncherContextMenu(
     ChromeLauncherController* controller,
     const ash::ShelfItem* item,
-    ash::Shelf* shelf)
-    : LauncherContextMenu(controller, item, shelf) {
+    int64_t display_id)
+    : LauncherContextMenu(controller, item, display_id) {
   Init();
 }
 
@@ -45,7 +45,11 @@
   if (item().type == ash::TYPE_PINNED_APP || item().type == ash::TYPE_APP) {
     // V1 apps can be started from the menu - but V2 apps should not.
     if (!controller()->IsPlatformApp(item().id)) {
-      AddItem(MENU_OPEN_NEW, base::string16());
+      int string_id = (GetLaunchType() == extensions::LAUNCH_TYPE_PINNED ||
+                       GetLaunchType() == extensions::LAUNCH_TYPE_REGULAR)
+                          ? IDS_APP_LIST_CONTEXT_MENU_NEW_TAB
+                          : IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW;
+      AddItemWithStringId(MENU_OPEN_NEW, string_id);
       AddSeparator(ui::NORMAL_SEPARATOR);
     }
 
@@ -84,8 +88,7 @@
     }
     if (!BrowserShortcutLauncherItemController(controller()->shelf_model())
              .IsListOfActiveBrowserEmpty()) {
-      AddItem(MENU_CLOSE,
-              l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_CLOSE));
+      AddItemWithStringId(MENU_CLOSE, IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
     }
   } else if (item().type == ash::TYPE_DIALOG) {
     AddItemWithStringId(MENU_CLOSE, IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
@@ -102,33 +105,6 @@
       AddSeparator(ui::NORMAL_SEPARATOR);
     }
   }
-  AddShelfOptionsMenu();
-}
-
-bool ExtensionLauncherContextMenu::IsItemForCommandIdDynamic(
-    int command_id) const {
-  return command_id == MENU_OPEN_NEW;
-}
-
-base::string16 ExtensionLauncherContextMenu::GetLabelForCommandId(
-    int command_id) const {
-  if (command_id == MENU_OPEN_NEW) {
-    if (controller()->IsPlatformApp(item().id))
-      return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW);
-    switch (GetLaunchType()) {
-      case extensions::LAUNCH_TYPE_PINNED:
-      case extensions::LAUNCH_TYPE_REGULAR:
-        return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_TAB);
-      case extensions::LAUNCH_TYPE_FULLSCREEN:
-      case extensions::LAUNCH_TYPE_WINDOW:
-        return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW);
-      default:
-        NOTREACHED();
-        return base::string16();
-    }
-  }
-  NOTREACHED();
-  return base::string16();
 }
 
 bool ExtensionLauncherContextMenu::IsCommandIdChecked(int command_id) const {
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h
index f6085d8..c6125878f 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h
@@ -11,13 +11,6 @@
 #include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
 #include "extensions/common/constants.h"
 
-class ChromeLauncherController;
-
-namespace ash {
-class Shelf;
-struct ShelfItem;
-}
-
 namespace extensions {
 class ContextMenuMatcher;
 }
@@ -27,12 +20,10 @@
  public:
   ExtensionLauncherContextMenu(ChromeLauncherController* controller,
                                const ash::ShelfItem* item,
-                               ash::Shelf* shelf);
+                               int64_t display_id);
   ~ExtensionLauncherContextMenu() override;
 
   // ui::SimpleMenuModel::Delegate overrides:
-  bool IsItemForCommandIdDynamic(int command_id) const override;
-  base::string16 GetLabelForCommandId(int command_id) const override;
   bool IsCommandIdChecked(int command_id) const override;
   bool IsCommandIdEnabled(int command_id) const override;
   void ExecuteCommand(int command_id, int event_flags) override;
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
index 31e1624..8825906a 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
@@ -7,103 +7,71 @@
 #include <string>
 
 #include "ash/public/cpp/shelf_model.h"
-#include "ash/public/cpp/shelf_prefs.h"
-#include "ash/shelf/shelf.h"
-#include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/wallpaper/wallpaper_delegate.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/metrics/user_metrics.h"
-#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
-#include "chrome/browser/fullscreen.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/launcher/arc_launcher_context_menu.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/desktop_shell_launcher_context_menu.h"
 #include "chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/session_manager/core/session_manager.h"
-#include "content/public/common/context_menu_params.h"
+#include "ui/display/types/display_constants.h"
 
 // static
-LauncherContextMenu* LauncherContextMenu::Create(
+std::unique_ptr<LauncherContextMenu> LauncherContextMenu::Create(
     ChromeLauncherController* controller,
     const ash::ShelfItem* item,
-    ash::Shelf* shelf) {
+    int64_t display_id) {
   DCHECK(controller);
-  DCHECK(shelf);
-  // Create DesktopShellLauncherContextMenu if no item is selected.
-  if (!item || item->id.IsNull())
-    return new DesktopShellLauncherContextMenu(controller, item, shelf);
+  DCHECK(item);
+  DCHECK(!item->id.IsNull());
+  // Create an ArcLauncherContextMenu if the item is an ARC app.
+  if (arc::IsArcItem(controller->profile(), item->id.app_id)) {
+    return base::MakeUnique<ArcLauncherContextMenu>(controller, item,
+                                                    display_id);
+  }
 
-  // Create ArcLauncherContextMenu if the item is an ARC app.
-  if (arc::IsArcItem(controller->profile(), item->id.app_id))
-    return new ArcLauncherContextMenu(controller, item, shelf);
-
-  // Create ExtensionLauncherContextMenu for the item.
-  return new ExtensionLauncherContextMenu(controller, item, shelf);
+  // Create an ExtensionLauncherContextMenu for other items.
+  return base::MakeUnique<ExtensionLauncherContextMenu>(controller, item,
+                                                        display_id);
 }
 
 LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller,
                                          const ash::ShelfItem* item,
-                                         ash::Shelf* shelf)
-    : ui::SimpleMenuModel(nullptr),
+                                         int64_t display_id)
+    : ui::SimpleMenuModel(this),
       controller_(controller),
       item_(item ? *item : ash::ShelfItem()),
-      shelf_alignment_menu_(shelf),
-      shelf_(shelf) {
-  set_delegate(this);
+      display_id_(display_id) {
+  DCHECK_NE(display_id, display::kInvalidDisplayId);
 }
 
 LauncherContextMenu::~LauncherContextMenu() {}
 
-bool LauncherContextMenu::IsItemForCommandIdDynamic(int command_id) const {
-  return false;
-}
-
-base::string16 LauncherContextMenu::GetLabelForCommandId(int command_id) const {
-  NOTREACHED();
-  return base::string16();
-}
-
 bool LauncherContextMenu::IsCommandIdChecked(int command_id) const {
-  if (command_id == MENU_AUTO_HIDE) {
-    return shelf_->auto_hide_behavior() == ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
-  }
   DCHECK(command_id < MENU_ITEM_COUNT);
   return false;
 }
 
 bool LauncherContextMenu::IsCommandIdEnabled(int command_id) const {
-  switch (command_id) {
-    case MENU_PIN:
-      // Users cannot modify the pinned state of apps pinned by policy.
-      return !item_.pinned_by_policy && (item_.type == ash::TYPE_PINNED_APP ||
-                                         item_.type == ash::TYPE_APP);
-    case MENU_CHANGE_WALLPAPER:
-      return ash::Shell::Get()->wallpaper_delegate()->CanOpenSetWallpaperPage();
-    case MENU_AUTO_HIDE:
-      return ash::CanUserModifyShelfAutoHideBehavior(
-          controller_->profile()->GetPrefs());
-    default:
-      DCHECK(command_id < MENU_ITEM_COUNT);
-      return true;
+  if (command_id == MENU_PIN) {
+    // Users cannot modify the pinned state of apps pinned by policy.
+    return !item_.pinned_by_policy &&
+           (item_.type == ash::TYPE_PINNED_APP || item_.type == ash::TYPE_APP);
   }
+
+  DCHECK(command_id < MENU_ITEM_COUNT);
+  return true;
 }
 
 void LauncherContextMenu::ExecuteCommand(int command_id, int event_flags) {
-  int64_t display_id =
-      display::Screen::GetScreen()
-          ->GetDisplayNearestWindow(shelf_->shelf_widget()->GetNativeWindow())
-          .id();
   switch (static_cast<MenuItem>(command_id)) {
     case MENU_OPEN_NEW:
       controller_->LaunchApp(item_.id, ash::LAUNCH_FROM_UNKNOWN, ui::EF_NONE,
-                             display_id);
+                             display_id_);
       break;
     case MENU_CLOSE:
       if (item_.type == ash::TYPE_DIALOG) {
@@ -116,7 +84,9 @@
         controller_->Close(item_.id);
       }
       base::RecordAction(base::UserMetricsAction("CloseFromContextMenu"));
-      if (ash::Shell::Get()
+      // TODO(mash): Support this tablet mode check.
+      if (ash::Shell::HasInstance() &&
+          ash::Shell::Get()
               ->tablet_mode_controller()
               ->IsTabletModeWindowManagerEnabled()) {
         base::RecordAction(
@@ -129,18 +99,6 @@
       else
         controller_->PinAppWithID(item_.id.app_id);
       break;
-    case MENU_AUTO_HIDE:
-      ash::SetShelfAutoHideBehaviorPref(
-          controller_->profile()->GetPrefs(), display_id,
-          shelf_->auto_hide_behavior() == ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
-              ? ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
-              : ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
-      break;
-    case MENU_ALIGNMENT_MENU:
-      break;
-    case MENU_CHANGE_WALLPAPER:
-      chromeos::WallpaperManager::Get()->Open();
-      break;
     default:
       NOTREACHED();
   }
@@ -168,39 +126,12 @@
   AddItemWithStringId(MENU_PIN, menu_pin_string_id);
 }
 
-void LauncherContextMenu::AddShelfOptionsMenu() {
-  // In fullscreen, the launcher is either hidden or auto hidden depending
-  // on the type of fullscreen. Do not show the auto-hide menu item while in
-  // fullscreen per display because it is confusing when the preference appears
-  // not to apply.
-  display::Display display =
-      display::Screen::GetScreen()->GetDisplayNearestWindow(
-          shelf_->GetWindow()->GetRootWindow());
-  if (!IsFullScreenMode(display.id()) &&
-      ash::CanUserModifyShelfAutoHideBehavior(
-          controller_->profile()->GetPrefs())) {
-    AddCheckItemWithStringId(MENU_AUTO_HIDE,
-                             IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE);
-  }
-  if (ash::Shelf::CanChangeShelfAlignment() &&
-      !session_manager::SessionManager::Get()->IsScreenLocked()) {
-    AddSubMenuWithStringId(MENU_ALIGNMENT_MENU,
-                           IDS_ASH_SHELF_CONTEXT_MENU_POSITION,
-                           &shelf_alignment_menu_);
-  }
-  if (!controller_->profile()->IsGuestSession())
-    AddItemWithStringId(MENU_CHANGE_WALLPAPER, IDS_AURA_SET_DESKTOP_WALLPAPER);
-}
-
 bool LauncherContextMenu::ExecuteCommonCommand(int command_id,
                                                int event_flags) {
   switch (command_id) {
     case MENU_OPEN_NEW:
     case MENU_CLOSE:
     case MENU_PIN:
-    case MENU_AUTO_HIDE:
-    case MENU_ALIGNMENT_MENU:
-    case MENU_CHANGE_WALLPAPER:
       LauncherContextMenu::ExecuteCommand(command_id, event_flags);
       return true;
     default:
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.h b/chrome/browser/ui/ash/launcher/launcher_context_menu.h
index c24279d..7f4cff0 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu.h
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.h
@@ -6,19 +6,12 @@
 #define CHROME_BROWSER_UI_ASH_LAUNCHER_LAUNCHER_CONTEXT_MENU_H_
 
 #include "ash/public/cpp/shelf_item.h"
-#include "ash/shelf/shelf_alignment_menu.h"
 #include "base/macros.h"
 #include "ui/base/models/simple_menu_model.h"
 
 class ChromeLauncherController;
 
-namespace ash {
-class Shelf;
-}
-
-// Base class for context menu which is shown for a regular extension item in
-// the shelf, or for an ARC app item in the shelf, or shown when right click
-// on desktop shell.
+// A base class for browser, extension, and ARC shelf item context menus.
 class LauncherContextMenu : public ui::SimpleMenuModel,
                             public ui::SimpleMenuModel::Delegate {
  public:
@@ -31,24 +24,20 @@
     LAUNCH_TYPE_REGULAR_TAB,
     LAUNCH_TYPE_FULLSCREEN,
     LAUNCH_TYPE_WINDOW,
-    MENU_AUTO_HIDE,
     MENU_NEW_WINDOW,
     MENU_NEW_INCOGNITO_WINDOW,
-    MENU_ALIGNMENT_MENU,
-    MENU_CHANGE_WALLPAPER,
     MENU_ITEM_COUNT
   };
 
   ~LauncherContextMenu() override;
 
-  // Static function to create contextmenu instance.
-  static LauncherContextMenu* Create(ChromeLauncherController* controller,
-                                     const ash::ShelfItem* item,
-                                     ash::Shelf* shelf);
+  // Static function to create a context menu instance.
+  static std::unique_ptr<LauncherContextMenu> Create(
+      ChromeLauncherController* controller,
+      const ash::ShelfItem* item,
+      int64_t display_id);
 
   // ui::SimpleMenuModel::Delegate overrides:
-  bool IsItemForCommandIdDynamic(int command_id) const override;
-  base::string16 GetLabelForCommandId(int command_id) const override;
   bool IsCommandIdChecked(int command_id) const override;
   bool IsCommandIdEnabled(int command_id) const override;
   void ExecuteCommand(int command_id, int event_flags) override;
@@ -56,28 +45,23 @@
  protected:
   LauncherContextMenu(ChromeLauncherController* controller,
                       const ash::ShelfItem* item,
-                      ash::Shelf* shelf);
-  ChromeLauncherController* controller() const { return controller_; }
+                      int64_t display_id);
 
+  ChromeLauncherController* controller() const { return controller_; }
   const ash::ShelfItem& item() const { return item_; }
 
   // Add menu item for pin/unpin.
   void AddPinMenu();
 
-  // Add common shelf options items, e.g. autohide, alignment, and wallpaper.
-  void AddShelfOptionsMenu();
-
   // Helper method to execute common commands. Returns true if handled.
   bool ExecuteCommonCommand(int command_id, int event_flags);
 
  private:
   ChromeLauncherController* controller_;
 
-  ash::ShelfItem item_;
+  const ash::ShelfItem item_;
 
-  ash::ShelfAlignmentMenu shelf_alignment_menu_;
-
-  ash::Shelf* shelf_;
+  const int64_t display_id_;
 
   DISALLOW_COPY_AND_ASSIGN(LauncherContextMenu);
 };
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
index 8f274e1..3668794 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/ui/ash/launcher/arc_launcher_context_menu.h"
 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
-#include "chrome/browser/ui/ash/launcher/desktop_shell_launcher_context_menu.h"
 #include "chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/arc/test/fake_app_instance.h"
@@ -77,25 +76,13 @@
     ash::AshTestBase::SetUp();
   }
 
-  ash::Shelf* GetShelf(int64_t display_id) {
-    ash::RootWindowController* root_window_controller =
-        ash::Shell::GetRootWindowControllerWithDisplayId(display_id);
-    EXPECT_NE(nullptr, root_window_controller);
-    return root_window_controller->shelf();
-  }
-
-  LauncherContextMenu* CreateLauncherContextMenu(
+  std::unique_ptr<LauncherContextMenu> CreateLauncherContextMenu(
       ash::ShelfItemType shelf_item_type,
-      ash::Shelf* shelf) {
+      int64_t display_id) {
     ash::ShelfItem item;
     item.id = ash::ShelfID("idmockidmockidmockidmockidmockid");
     item.type = shelf_item_type;
-    return LauncherContextMenu::Create(controller(), &item, shelf);
-  }
-
-  LauncherContextMenu* CreateLauncherContextMenuForDesktopShell(
-      ash::Shelf* shelf) {
-    return LauncherContextMenu::Create(controller(), nullptr, shelf);
+    return LauncherContextMenu::Create(controller(), &item, display_id);
   }
 
   // Creates app window and set optional ARC application id.
@@ -133,8 +120,8 @@
        NewIncognitoWindowMenuIsDisabledWhenIncognitoModeOff) {
   int64_t primary_id = GetPrimaryDisplay().id();
   // Initially, "New Incognito window" should be enabled.
-  std::unique_ptr<LauncherContextMenu> menu(CreateLauncherContextMenu(
-      ash::TYPE_BROWSER_SHORTCUT, GetShelf(primary_id)));
+  std::unique_ptr<LauncherContextMenu> menu =
+      CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT, primary_id);
   ASSERT_TRUE(IsItemPresentInMenu(
       menu.get(), LauncherContextMenu::MENU_NEW_INCOGNITO_WINDOW));
   EXPECT_TRUE(menu->IsCommandIdEnabled(
@@ -143,8 +130,7 @@
   // Disable Incognito mode.
   IncognitoModePrefs::SetAvailability(profile()->GetPrefs(),
                                       IncognitoModePrefs::DISABLED);
-  menu.reset(CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT,
-                                       GetShelf(primary_id)));
+  menu = CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT, primary_id);
   // The item should be disabled.
   ASSERT_TRUE(IsItemPresentInMenu(
       menu.get(), LauncherContextMenu::MENU_NEW_INCOGNITO_WINDOW));
@@ -158,8 +144,8 @@
        NewWindowMenuIsDisabledWhenIncognitoModeForced) {
   int64_t primary_id = GetPrimaryDisplay().id();
   // Initially, "New window" should be enabled.
-  std::unique_ptr<LauncherContextMenu> menu(CreateLauncherContextMenu(
-      ash::TYPE_BROWSER_SHORTCUT, GetShelf(primary_id)));
+  std::unique_ptr<LauncherContextMenu> menu =
+      CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT, primary_id);
   ASSERT_TRUE(IsItemPresentInMenu(
       menu.get(), LauncherContextMenu::MENU_NEW_WINDOW));
   EXPECT_TRUE(menu->IsCommandIdEnabled(LauncherContextMenu::MENU_NEW_WINDOW));
@@ -167,43 +153,19 @@
   // Disable Incognito mode.
   IncognitoModePrefs::SetAvailability(profile()->GetPrefs(),
                                       IncognitoModePrefs::FORCED);
-  menu.reset(CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT,
-                                       GetShelf(primary_id)));
+  menu = CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT, primary_id);
   ASSERT_TRUE(IsItemPresentInMenu(
       menu.get(), LauncherContextMenu::MENU_NEW_WINDOW));
   EXPECT_FALSE(menu->IsCommandIdEnabled(LauncherContextMenu::MENU_NEW_WINDOW));
 }
 
-// Verifies status of contextmenu items for desktop shell.
-TEST_F(LauncherContextMenuTest, DesktopShellLauncherContextMenuItemCheck) {
-  int64_t primary_id = GetPrimaryDisplay().id();
-  std::unique_ptr<LauncherContextMenu> menu(
-      CreateLauncherContextMenuForDesktopShell(GetShelf(primary_id)));
-  EXPECT_FALSE(
-      IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_OPEN_NEW));
-  EXPECT_FALSE(IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_PIN));
-  EXPECT_TRUE(
-      IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_AUTO_HIDE));
-  EXPECT_TRUE(menu->IsCommandIdEnabled(LauncherContextMenu::MENU_AUTO_HIDE));
-  EXPECT_TRUE(IsItemPresentInMenu(menu.get(),
-                                  LauncherContextMenu::MENU_ALIGNMENT_MENU));
-  EXPECT_TRUE(
-      menu->IsCommandIdEnabled(LauncherContextMenu::MENU_ALIGNMENT_MENU));
-  // By default, screen is not locked and ChangeWallPaper item is added in
-  // menu. ChangeWallPaper item is not enabled in default mode.
-  EXPECT_TRUE(IsItemPresentInMenu(menu.get(),
-                                  LauncherContextMenu::MENU_CHANGE_WALLPAPER));
-  EXPECT_FALSE(
-      menu->IsCommandIdEnabled(LauncherContextMenu::MENU_CHANGE_WALLPAPER));
-}
-
 // Verifies that "Close" is not shown in context menu if no browser window is
 // opened.
 TEST_F(LauncherContextMenuTest,
        DesktopShellLauncherContextMenuVerifyCloseItem) {
   int64_t primary_id = GetPrimaryDisplay().id();
-  std::unique_ptr<LauncherContextMenu> menu(CreateLauncherContextMenu(
-      ash::TYPE_BROWSER_SHORTCUT, GetShelf(primary_id)));
+  std::unique_ptr<LauncherContextMenu> menu =
+      CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT, primary_id);
   ASSERT_FALSE(
       IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_CLOSE));
 }
@@ -221,10 +183,8 @@
   const ash::ShelfItem* item = controller()->GetItem(ash::ShelfID(app_id));
   ASSERT_TRUE(item);
   int64_t primary_id = GetPrimaryDisplay().id();
-  ash::Shelf* shelf = GetShelf(primary_id);
-
-  std::unique_ptr<LauncherContextMenu> menu(
-      new ArcLauncherContextMenu(controller(), item, shelf));
+  std::unique_ptr<LauncherContextMenu> menu =
+      base::MakeUnique<ArcLauncherContextMenu>(controller(), item, primary_id);
 
   // ARC app is pinned but not running.
   EXPECT_TRUE(
@@ -235,26 +195,13 @@
   EXPECT_FALSE(
       IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_CLOSE));
 
-  EXPECT_TRUE(
-      IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_AUTO_HIDE));
-  EXPECT_TRUE(menu->IsCommandIdEnabled(LauncherContextMenu::MENU_AUTO_HIDE));
-  EXPECT_TRUE(IsItemPresentInMenu(menu.get(),
-                                  LauncherContextMenu::MENU_ALIGNMENT_MENU));
-  EXPECT_TRUE(
-      menu->IsCommandIdEnabled(LauncherContextMenu::MENU_ALIGNMENT_MENU));
-  // By default, screen is not locked and ChangeWallPaper item is added in
-  // menu. ChangeWallPaper item is not enabled in default mode.
-  EXPECT_TRUE(IsItemPresentInMenu(menu.get(),
-                                  LauncherContextMenu::MENU_CHANGE_WALLPAPER));
-  EXPECT_FALSE(
-      menu->IsCommandIdEnabled(LauncherContextMenu::MENU_CHANGE_WALLPAPER));
-
   // ARC app is running.
   std::string window_app_id1("org.chromium.arc.1");
   CreateArcWindow(window_app_id1);
   arc_test().app_instance()->SendTaskCreated(1, arc_test().fake_apps()[0],
                                              std::string());
-  menu.reset(new ArcLauncherContextMenu(controller(), item, shelf));
+  menu =
+      base::MakeUnique<ArcLauncherContextMenu>(controller(), item, primary_id);
 
   EXPECT_FALSE(
       IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_OPEN_NEW));
@@ -271,7 +218,8 @@
                                              std::string());
   const ash::ShelfItem* item2 = controller()->GetItem(ash::ShelfID(app_id2));
   ASSERT_TRUE(item2);
-  menu.reset(new ArcLauncherContextMenu(controller(), item2, shelf));
+  menu =
+      base::MakeUnique<ArcLauncherContextMenu>(controller(), item2, primary_id);
 
   EXPECT_FALSE(
       IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_OPEN_NEW));
@@ -294,7 +242,9 @@
                                              shortcuts[0].intent_uri);
   const ash::ShelfItem* item3 = controller()->GetItem(ash::ShelfID(app_id3));
   ASSERT_TRUE(item3);
-  menu.reset(new ArcLauncherContextMenu(controller(), item3, shelf));
+
+  menu =
+      base::MakeUnique<ArcLauncherContextMenu>(controller(), item3, primary_id);
 
   EXPECT_FALSE(
       IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_OPEN_NEW));
@@ -303,29 +253,4 @@
   EXPECT_TRUE(menu->IsCommandIdEnabled(LauncherContextMenu::MENU_CLOSE));
 }
 
-// Tests that fullscreen which makes "Autohide shelf" option disappeared on
-// shelf is a per-display setting (crbug.com/496681).
-TEST_F(LauncherContextMenuTest, AutohideShelfOptionOnExternalDisplay) {
-  UpdateDisplay("940x550,940x550");
-  int64_t primary_id = GetPrimaryDisplay().id();
-  int64_t secondary_id = GetSecondaryDisplay().id();
-
-  // Create a normal window on primary display.
-  views::Widget* widget = new views::Widget;
-  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
-  params.context = ash::Shell::GetPrimaryRootWindow();
-  widget->Init(params);
-  widget->Show();
-
-  widget->SetFullscreen(true);
-  std::unique_ptr<LauncherContextMenu> primary_menu(CreateLauncherContextMenu(
-      ash::TYPE_BROWSER_SHORTCUT, GetShelf(primary_id)));
-  std::unique_ptr<LauncherContextMenu> secondary_menu(CreateLauncherContextMenu(
-      ash::TYPE_BROWSER_SHORTCUT, GetShelf(secondary_id)));
-  EXPECT_FALSE(IsItemPresentInMenu(primary_menu.get(),
-                                   LauncherContextMenu::MENU_AUTO_HIDE));
-  EXPECT_TRUE(IsItemPresentInMenu(secondary_menu.get(),
-                                  LauncherContextMenu::MENU_AUTO_HIDE));
-}
-
 }  // namespace