1diff --git chrome/browser/renderer_context_menu/render_view_context_menu.cc chrome/browser/renderer_context_menu/render_view_context_menu.cc 2index 13ad2a7529bb..0b003c26815e 100644 3--- chrome/browser/renderer_context_menu/render_view_context_menu.cc 4+++ chrome/browser/renderer_context_menu/render_view_context_menu.cc 5@@ -254,6 +254,13 @@ base::OnceCallback<void(RenderViewContextMenu*)>* GetMenuShownCallback() { 6 return callback.get(); 7 } 8 9+ 10+RenderViewContextMenu::MenuCreatedCallback* GetMenuCreatedCallback() { 11+ static base::NoDestructor<RenderViewContextMenu::MenuCreatedCallback> 12+ callback; 13+ return callback.get(); 14+} 15+ 16 enum class UmaEnumIdLookupType { 17 GeneralEnumId, 18 ContextSpecificEnumId, 19@@ -463,6 +470,10 @@ int FindUMAEnumValueForCommand(int id, UmaEnumIdLookupType type) { 20 if (ContextMenuMatcher::IsExtensionsCustomCommandId(id)) 21 return 1; 22 23+ // Match the MENU_ID_USER_FIRST to MENU_ID_USER_LAST range from cef_types.h. 24+ if (id >= 26500 && id <= 28500) 25+ return 1; 26+ 27 id = CollapseCommandsForUMA(id); 28 const auto& map = GetIdcToUmaMap(type); 29 auto it = map.find(id); 30@@ -618,6 +629,14 @@ RenderViewContextMenu::RenderViewContextMenu( 31 } 32 set_content_type( 33 ContextMenuContentTypeFactory::Create(source_web_contents_, params)); 34+ 35+ auto* cb = GetMenuCreatedCallback(); 36+ if (!cb->is_null()) { 37+ first_observer_ = cb->Run(this); 38+ if (first_observer_) { 39+ observers_.AddObserver(first_observer_.get()); 40+ } 41+ } 42 } 43 44 RenderViewContextMenu::~RenderViewContextMenu() = default; 45@@ -973,6 +992,12 @@ void RenderViewContextMenu::InitMenu() { 46 // menu, meaning that each menu item added/removed in this function will cause 47 // it to visibly jump on the screen (see b/173569669). 48 AppendQuickAnswersItems(); 49+ 50+ if (first_observer_) { 51+ // Do this last so that the observer can optionally modify previously 52+ // created items. 53+ first_observer_->InitMenu(params_); 54+ } 55 } 56 57 Profile* RenderViewContextMenu::GetProfile() const { 58@@ -2594,6 +2619,12 @@ void RenderViewContextMenu::RegisterMenuShownCallbackForTesting( 59 *GetMenuShownCallback() = std::move(cb); 60 } 61 62+// static 63+void RenderViewContextMenu::RegisterMenuCreatedCallback( 64+ MenuCreatedCallback cb) { 65+ *GetMenuCreatedCallback() = cb; 66+} 67+ 68 ProtocolHandlerRegistry::ProtocolHandlerList 69 RenderViewContextMenu::GetHandlersForLinkUrl() { 70 ProtocolHandlerRegistry::ProtocolHandlerList handlers = 71diff --git chrome/browser/renderer_context_menu/render_view_context_menu.h chrome/browser/renderer_context_menu/render_view_context_menu.h 72index 5178db60ba64..845461049e54 100644 73--- chrome/browser/renderer_context_menu/render_view_context_menu.h 74+++ chrome/browser/renderer_context_menu/render_view_context_menu.h 75@@ -91,6 +91,12 @@ class RenderViewContextMenu : public RenderViewContextMenuBase { 76 static void RegisterMenuShownCallbackForTesting( 77 base::OnceCallback<void(RenderViewContextMenu*)> cb); 78 79+ // Registers a callback that will be called each time a context menu is 80+ // created. 81+ using MenuCreatedCallback = base::RepeatingCallback< 82+ std::unique_ptr<RenderViewContextMenuObserver>(RenderViewContextMenu*)>; 83+ static void RegisterMenuCreatedCallback(MenuCreatedCallback cb); 84+ 85 protected: 86 Profile* GetProfile() const; 87 88@@ -265,6 +271,9 @@ class RenderViewContextMenu : public RenderViewContextMenuBase { 89 ui::SimpleMenuModel protocol_handler_submenu_model_; 90 ProtocolHandlerRegistry* protocol_handler_registry_; 91 92+ // An observer returned via MenuCreatedCallback that will be called first. 93+ std::unique_ptr<RenderViewContextMenuObserver> first_observer_; 94+ 95 // An observer that handles spelling suggestions, "Add to dictionary", and 96 // "Use enhanced spell check" items. 97 std::unique_ptr<SpellingMenuObserver> spelling_suggestions_menu_observer_; 98diff --git chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc 99index feec153dcc14..0959c1020bad 100644 100--- chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc 101+++ chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc 102@@ -136,6 +136,9 @@ void RenderViewContextMenuViews::RunMenuAt(views::Widget* parent, 103 bool RenderViewContextMenuViews::GetAcceleratorForCommandId( 104 int command_id, 105 ui::Accelerator* accel) const { 106+ if (RenderViewContextMenu::GetAcceleratorForCommandId(command_id, accel)) 107+ return true; 108+ 109 // There are no formally defined accelerators we can query so we assume 110 // that Ctrl+C, Ctrl+V, Ctrl+X, Ctrl-A, etc do what they normally do. 111 switch (command_id) { 112diff --git components/renderer_context_menu/render_view_context_menu_base.cc components/renderer_context_menu/render_view_context_menu_base.cc 113index 62100403d27f..54182e7d97e3 100644 114--- components/renderer_context_menu/render_view_context_menu_base.cc 115+++ components/renderer_context_menu/render_view_context_menu_base.cc 116@@ -375,6 +375,17 @@ bool RenderViewContextMenuBase::IsCommandIdChecked(int id) const { 117 return false; 118 } 119 120+bool RenderViewContextMenuBase::GetAcceleratorForCommandId( 121+ int id, 122+ ui::Accelerator* accelerator) const { 123+ for (auto& observer : observers_) { 124+ if (observer.IsCommandIdSupported(id)) 125+ return observer.GetAccelerator(id, accelerator); 126+ } 127+ 128+ return false; 129+} 130+ 131 void RenderViewContextMenuBase::ExecuteCommand(int id, int event_flags) { 132 command_executed_ = true; 133 RecordUsedItem(id); 134diff --git components/renderer_context_menu/render_view_context_menu_base.h components/renderer_context_menu/render_view_context_menu_base.h 135index 52002b190fde..ce3277e9f935 100644 136--- components/renderer_context_menu/render_view_context_menu_base.h 137+++ components/renderer_context_menu/render_view_context_menu_base.h 138@@ -81,6 +81,9 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate, 139 140 const ui::SimpleMenuModel& menu_model() const { return menu_model_; } 141 const content::ContextMenuParams& params() const { return params_; } 142+ content::WebContents* source_web_contents() const { 143+ return source_web_contents_; 144+ } 145 146 // Returns true if the specified command id is known and valid for 147 // this menu. If the command is known |enabled| is set to indicate 148@@ -89,6 +92,9 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate, 149 150 // SimpleMenuModel::Delegate implementation. 151 bool IsCommandIdChecked(int command_id) const override; 152+ bool GetAcceleratorForCommandId( 153+ int command_id, 154+ ui::Accelerator* accelerator) const override; 155 void ExecuteCommand(int command_id, int event_flags) override; 156 void OnMenuWillShow(ui::SimpleMenuModel* source) override; 157 void MenuClosed(ui::SimpleMenuModel* source) override; 158@@ -119,6 +125,9 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate, 159 content::WebContents* GetWebContents() const override; 160 content::BrowserContext* GetBrowserContext() const override; 161 162+ // May return nullptr if the frame was deleted while the menu was open. 163+ content::RenderFrameHost* GetRenderFrameHost() const; 164+ 165 protected: 166 friend class RenderViewContextMenuTest; 167 friend class RenderViewContextMenuPrefsTest; 168@@ -156,9 +165,6 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate, 169 // TODO(oshima): Remove this. 170 virtual void AppendPlatformEditableItems() {} 171 172- // May return nullptr if the frame was deleted while the menu was open. 173- content::RenderFrameHost* GetRenderFrameHost() const; 174- 175 bool IsCustomItemChecked(int id) const; 176 bool IsCustomItemEnabled(int id) const; 177 178diff --git components/renderer_context_menu/render_view_context_menu_observer.cc components/renderer_context_menu/render_view_context_menu_observer.cc 179index 2e2d05f91c64..85b256b2be9b 100644 180--- components/renderer_context_menu/render_view_context_menu_observer.cc 181+++ components/renderer_context_menu/render_view_context_menu_observer.cc 182@@ -15,3 +15,8 @@ bool RenderViewContextMenuObserver::IsCommandIdChecked(int command_id) { 183 bool RenderViewContextMenuObserver::IsCommandIdEnabled(int command_id) { 184 return false; 185 } 186+ 187+bool RenderViewContextMenuObserver::GetAccelerator(int command_id, 188+ ui::Accelerator* accel) { 189+ return false; 190+} 191diff --git components/renderer_context_menu/render_view_context_menu_observer.h components/renderer_context_menu/render_view_context_menu_observer.h 192index b360a8eb4e82..6f9023a62904 100644 193--- components/renderer_context_menu/render_view_context_menu_observer.h 194+++ components/renderer_context_menu/render_view_context_menu_observer.h 195@@ -11,6 +11,10 @@ namespace content { 196 struct ContextMenuParams; 197 } 198 199+namespace ui { 200+class Accelerator; 201+} 202+ 203 // The interface used for implementing context-menu items. The following 204 // instruction describe how to implement a context-menu item with this 205 // interface. 206@@ -100,6 +104,8 @@ class RenderViewContextMenuObserver { 207 virtual bool IsCommandIdChecked(int command_id); 208 virtual bool IsCommandIdEnabled(int command_id); 209 210+ virtual bool GetAccelerator(int command_id, ui::Accelerator* accel); 211+ 212 // Called when a user selects the specified context-menu item. This is 213 // only called when the observer returns true for IsCommandIdSupported() 214 // for that |command_id|. 215