1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10 #include <memory>
11 #include <set>
12 #include <utility>
13
14 #include "base/bind.h"
15 #include "base/command_line.h"
16 #include "base/feature_list.h"
17 #include "base/logging.h"
18 #include "base/metrics/field_trial.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/metrics/user_metrics.h"
21 #include "base/no_destructor.h"
22 #include "base/stl_util.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "build/branding_buildflags.h"
27 #include "build/build_config.h"
28 #include "chrome/app/chrome_command_ids.h"
29 #include "chrome/app/vector_icons/vector_icons.h"
30 #include "chrome/browser/app_mode/app_mode_utils.h"
31 #include "chrome/browser/apps/app_service/app_launch_params.h"
32 #include "chrome/browser/apps/app_service/app_service_proxy.h"
33 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
34 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
35 #include "chrome/browser/apps/platform_apps/app_load_service.h"
36 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
37 #include "chrome/browser/browser_features.h"
38 #include "chrome/browser/browser_process.h"
39 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
40 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
41 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
42 #include "chrome/browser/devtools/devtools_window.h"
43 #include "chrome/browser/download/download_stats.h"
44 #include "chrome/browser/language/language_model_manager_factory.h"
45 #include "chrome/browser/media/router/media_router_feature.h"
46 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
47 #include "chrome/browser/platform_util.h"
48 #include "chrome/browser/prefs/incognito_mode_prefs.h"
49 #include "chrome/browser/profiles/profile.h"
50 #include "chrome/browser/profiles/profile_attributes_entry.h"
51 #include "chrome/browser/profiles/profile_attributes_storage.h"
52 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
53 #include "chrome/browser/profiles/profile_io_data.h"
54 #include "chrome/browser/profiles/profile_manager.h"
55 #include "chrome/browser/profiles/profile_window.h"
56 #include "chrome/browser/renderer_context_menu/accessibility_labels_menu_observer.h"
57 #include "chrome/browser/renderer_context_menu/context_menu_content_type_factory.h"
58 #include "chrome/browser/renderer_context_menu/copy_link_to_text_menu_observer.h"
59 #include "chrome/browser/renderer_context_menu/spelling_menu_observer.h"
60 #include "chrome/browser/search/search.h"
61 #include "chrome/browser/search_engines/template_url_service_factory.h"
62 #include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
63 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
64 #include "chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h"
65 #include "chrome/browser/sharing/click_to_call/click_to_call_metrics.h"
66 #include "chrome/browser/sharing/click_to_call/click_to_call_utils.h"
67 #include "chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h"
68 #include "chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h"
69 #include "chrome/browser/spellchecker/spellcheck_service.h"
70 #include "chrome/browser/translate/chrome_translate_client.h"
71 #include "chrome/browser/translate/translate_service.h"
72 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
73 #include "chrome/browser/ui/browser.h"
74 #include "chrome/browser/ui/browser_commands.h"
75 #include "chrome/browser/ui/browser_finder.h"
76 #include "chrome/browser/ui/browser_navigator_params.h"
77 #include "chrome/browser/ui/chrome_pages.h"
78 #include "chrome/browser/ui/exclusive_access/keyboard_lock_controller.h"
79 #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
80 #include "chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.h"
81 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
82 #include "chrome/browser/ui/tabs/tab_strip_model.h"
83 #include "chrome/browser/ui/webui/history/foreign_session_handler.h"
84 #include "chrome/browser/web_applications/components/app_icon_manager.h"
85 #include "chrome/browser/web_applications/components/app_registrar.h"
86 #include "chrome/browser/web_applications/components/web_app_helpers.h"
87 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
88 #include "chrome/common/chrome_constants.h"
89 #include "chrome/common/chrome_features.h"
90 #include "chrome/common/chrome_render_frame.mojom.h"
91 #include "chrome/common/chrome_switches.h"
92 #include "chrome/common/content_restriction.h"
93 #include "chrome/common/pref_names.h"
94 #include "chrome/common/url_constants.h"
95 #include "chrome/common/webui_url_constants.h"
96 #include "chrome/grit/chromium_strings.h"
97 #include "chrome/grit/generated_resources.h"
98 #include "components/arc/arc_features.h"
99 #include "components/autofill/core/browser/ui/popup_item_ids.h"
100 #include "components/autofill/core/common/password_generation_util.h"
101 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
102 #include "components/download/public/common/download_url_parameters.h"
103 #include "components/google/core/common/google_util.h"
104 #include "components/guest_view/browser/guest_view_base.h"
105 #include "components/language/core/browser/language_model_manager.h"
106 #include "components/media_router/browser/media_router_dialog_controller.h"
107 #include "components/media_router/browser/media_router_metrics.h"
108 #include "components/omnibox/browser/autocomplete_classifier.h"
109 #include "components/omnibox/browser/autocomplete_match.h"
110 #include "components/password_manager/content/browser/content_password_manager_driver.h"
111 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
112 #include "components/password_manager/core/browser/password_manager_util.h"
113 #include "components/policy/content/policy_blocklist_service.h"
114 #include "components/prefs/pref_member.h"
115 #include "components/prefs/pref_service.h"
116 #include "components/search_engines/search_engines_pref_names.h"
117 #include "components/search_engines/template_url.h"
118 #include "components/search_engines/template_url_service.h"
119 #include "components/spellcheck/browser/pref_names.h"
120 #include "components/spellcheck/browser/spellcheck_host_metrics.h"
121 #include "components/spellcheck/common/spellcheck_common.h"
122 #include "components/spellcheck/spellcheck_buildflags.h"
123 #include "components/strings/grit/components_strings.h"
124 #include "components/translate/core/browser/translate_download_manager.h"
125 #include "components/translate/core/browser/translate_manager.h"
126 #include "components/translate/core/browser/translate_prefs.h"
127 #include "components/url_formatter/url_formatter.h"
128 #include "components/user_prefs/user_prefs.h"
129 #include "components/web_modal/web_contents_modal_dialog_manager.h"
130 #include "content/public/browser/child_process_security_policy.h"
131 #include "content/public/browser/download_manager.h"
132 #include "content/public/browser/navigation_details.h"
133 #include "content/public/browser/navigation_entry.h"
134 #include "content/public/browser/picture_in_picture_window_controller.h"
135 #include "content/public/browser/render_frame_host.h"
136 #include "content/public/browser/render_process_host.h"
137 #include "content/public/browser/render_view_host.h"
138 #include "content/public/browser/render_widget_host_view.h"
139 #include "content/public/browser/ssl_status.h"
140 #include "content/public/browser/web_contents.h"
141 #include "content/public/common/menu_item.h"
142 #include "content/public/common/url_utils.h"
143 #include "extensions/buildflags/buildflags.h"
144 #include "media/base/media_switches.h"
145 #include "mojo/public/cpp/bindings/associated_remote.h"
146 #include "net/base/escape.h"
147 #include "net/traffic_annotation/network_traffic_annotation.h"
148 #include "ppapi/buildflags/buildflags.h"
149 #include "printing/buildflags/buildflags.h"
150 #include "services/service_manager/public/cpp/interface_provider.h"
151 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
152 #include "third_party/blink/public/common/context_menu_data/edit_flags.h"
153 #include "third_party/blink/public/common/context_menu_data/input_field_type.h"
154 #include "third_party/blink/public/common/context_menu_data/media_type.h"
155 #include "third_party/blink/public/mojom/frame/media_player_action.mojom.h"
156 #include "third_party/blink/public/public_buildflags.h"
157 #include "third_party/metrics_proto/omnibox_input_type.pb.h"
158 #include "ui/base/clipboard/clipboard.h"
159 #include "ui/base/clipboard/scoped_clipboard_writer.h"
160 #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
161 #include "ui/base/emoji/emoji_panel_helper.h"
162 #include "ui/base/l10n/l10n_util.h"
163 #include "ui/base/models/image_model.h"
164 #include "ui/gfx/favicon_size.h"
165 #include "ui/gfx/geometry/point.h"
166 #include "ui/gfx/image/image.h"
167 #include "ui/gfx/text_elider.h"
168 #include "ui/strings/grit/ui_strings.h"
169
170 #if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
171 #include "chrome/browser/renderer_context_menu/spelling_options_submenu_observer.h"
172 #endif
173
174 #if BUILDFLAG(ENABLE_EXTENSIONS)
175 #include "chrome/browser/apps/app_service/app_launch_params.h"
176 #include "chrome/browser/extensions/devtools_util.h"
177 #include "chrome/browser/extensions/extension_service.h"
178 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
179 #include "extensions/browser/extension_host.h"
180 #include "extensions/browser/extension_registry.h"
181 #include "extensions/browser/extension_system.h"
182 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
183 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
184 #include "extensions/browser/view_type_utils.h"
185 #include "extensions/common/extension.h"
186 #endif
187
188 #if BUILDFLAG(ENABLE_PLUGINS)
189 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
190 #endif
191
192 #if BUILDFLAG(ENABLE_PRINTING)
193 #include "chrome/browser/printing/print_view_manager_common.h"
194 #include "components/printing/common/print.mojom.h"
195
196 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
197 #include "chrome/browser/printing/print_preview_context_menu_observer.h"
198 #include "chrome/browser/printing/print_preview_dialog_controller.h"
199 #endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
200 #endif // BUILDFLAG(ENABLE_PRINTING)
201
202 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
203 #include "chrome/browser/supervised_user/supervised_user_service.h"
204 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
205 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
206 #endif
207
208 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
209 #include "chrome/grit/theme_resources.h"
210 #include "ui/base/resource/resource_bundle.h"
211 #endif
212
213 #if defined(OS_CHROMEOS)
214 #include "ash/public/cpp/clipboard_history_controller.h"
215 #include "chrome/browser/chromeos/arc/arc_util.h"
216 #include "chrome/browser/chromeos/arc/intent_helper/open_with_menu.h"
217 #include "chrome/browser/chromeos/arc/intent_helper/start_smart_selection_action_menu.h"
218 #include "chrome/browser/renderer_context_menu/quick_answers_menu_observer.h"
219 #include "chromeos/constants/chromeos_features.h"
220 #include "ui/views/controls/menu/menu_types.h"
221 #endif
222
223 using base::UserMetricsAction;
224 using blink::ContextMenuDataEditFlags;
225 using blink::ContextMenuDataMediaType;
226 using blink::WebContextMenuData;
227 using blink::WebString;
228 using blink::WebURL;
229 using content::BrowserContext;
230 using content::ChildProcessSecurityPolicy;
231 using content::DownloadManager;
232 using content::NavigationEntry;
233 using content::OpenURLParams;
234 using content::RenderFrameHost;
235 using content::RenderViewHost;
236 using content::SSLStatus;
237 using content::WebContents;
238 using download::DownloadUrlParameters;
239 using extensions::ContextMenuMatcher;
240 using extensions::Extension;
241 using extensions::MenuItem;
242 using extensions::MenuManager;
243
244 namespace {
245
GetMenuShownCallback()246 base::OnceCallback<void(RenderViewContextMenu*)>* GetMenuShownCallback() {
247 static base::NoDestructor<base::OnceCallback<void(RenderViewContextMenu*)>>
248 callback;
249 return callback.get();
250 }
251
252 enum class UmaEnumIdLookupType {
253 GeneralEnumId,
254 ContextSpecificEnumId,
255 };
256
GetIdcToUmaMap(UmaEnumIdLookupType type)257 const std::map<int, int>& GetIdcToUmaMap(UmaEnumIdLookupType type) {
258 // These maps are from IDC_* -> UMA value. Never alter UMA ids. You may remove
259 // items, but add a line to keep the old value from being reused.
260
261 // These UMA values are for the the RenderViewContextMenuItem enum, used for
262 // the RenderViewContextMenu.Shown and RenderViewContextMenu.Used histograms.
263 static const base::NoDestructor<std::map<int, int>> kGeneralMap(
264 {// NB: UMA values for 0 and 1 are detected using
265 // RenderViewContextMenu::IsContentCustomCommandId() and
266 // ContextMenuMatcher::IsExtensionsCustomCommandId()
267 {IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST, 2},
268 {IDC_CONTENT_CONTEXT_OPENLINKNEWTAB, 3},
269 {IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW, 4},
270 {IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD, 5},
271 {IDC_CONTENT_CONTEXT_SAVELINKAS, 6},
272 {IDC_CONTENT_CONTEXT_SAVEAVAS, 7},
273 {IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 8},
274 {IDC_CONTENT_CONTEXT_COPYLINKLOCATION, 9},
275 {IDC_CONTENT_CONTEXT_COPYIMAGELOCATION, 10},
276 {IDC_CONTENT_CONTEXT_COPYAVLOCATION, 11},
277 {IDC_CONTENT_CONTEXT_COPYIMAGE, 12},
278 {IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB, 13},
279 {IDC_CONTENT_CONTEXT_OPENAVNEWTAB, 14},
280 {IDC_CONTENT_CONTEXT_PLAYPAUSE, 15},
281 {IDC_CONTENT_CONTEXT_MUTE, 16},
282 {IDC_CONTENT_CONTEXT_LOOP, 17},
283 {IDC_CONTENT_CONTEXT_CONTROLS, 18},
284 {IDC_CONTENT_CONTEXT_ROTATECW, 19},
285 {IDC_CONTENT_CONTEXT_ROTATECCW, 20},
286 {IDC_BACK, 21},
287 {IDC_FORWARD, 22},
288 {IDC_SAVE_PAGE, 23},
289 {IDC_RELOAD, 24},
290 {IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP, 25},
291 {IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP, 26},
292 {IDC_PRINT, 27},
293 {IDC_VIEW_SOURCE, 28},
294 {IDC_CONTENT_CONTEXT_INSPECTELEMENT, 29},
295 {IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE, 30},
296 {IDC_CONTENT_CONTEXT_VIEWPAGEINFO, 31},
297 {IDC_CONTENT_CONTEXT_TRANSLATE, 32},
298 {IDC_CONTENT_CONTEXT_RELOADFRAME, 33},
299 {IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE, 34},
300 {IDC_CONTENT_CONTEXT_VIEWFRAMEINFO, 35},
301 {IDC_CONTENT_CONTEXT_UNDO, 36},
302 {IDC_CONTENT_CONTEXT_REDO, 37},
303 {IDC_CONTENT_CONTEXT_CUT, 38},
304 {IDC_CONTENT_CONTEXT_COPY, 39},
305 {IDC_CONTENT_CONTEXT_PASTE, 40},
306 {IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE, 41},
307 {IDC_CONTENT_CONTEXT_DELETE, 42},
308 {IDC_CONTENT_CONTEXT_SELECTALL, 43},
309 {IDC_CONTENT_CONTEXT_SEARCHWEBFOR, 44},
310 {IDC_CONTENT_CONTEXT_GOTOURL, 45},
311 {IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS, 46},
312 {IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS, 47},
313 {IDC_CONTENT_CONTEXT_OPENLINKWITH, 52},
314 {IDC_CHECK_SPELLING_WHILE_TYPING, 53},
315 {IDC_SPELLCHECK_MENU, 54},
316 {IDC_CONTENT_CONTEXT_SPELLING_TOGGLE, 55},
317 {IDC_SPELLCHECK_LANGUAGES_FIRST, 56},
318 {IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE, 57},
319 {IDC_SPELLCHECK_SUGGESTION_0, 58},
320 {IDC_SPELLCHECK_ADD_TO_DICTIONARY, 59},
321 {IDC_SPELLPANEL_TOGGLE, 60},
322 {IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB, 61},
323 {IDC_WRITING_DIRECTION_MENU, 62},
324 {IDC_WRITING_DIRECTION_DEFAULT, 63},
325 {IDC_WRITING_DIRECTION_LTR, 64},
326 {IDC_WRITING_DIRECTION_RTL, 65},
327 {IDC_CONTENT_CONTEXT_LOAD_IMAGE, 66},
328 {IDC_ROUTE_MEDIA, 68},
329 {IDC_CONTENT_CONTEXT_COPYLINKTEXT, 69},
330 {IDC_CONTENT_CONTEXT_OPENLINKINPROFILE, 70},
331 {IDC_OPEN_LINK_IN_PROFILE_FIRST, 71},
332 {IDC_CONTENT_CONTEXT_GENERATEPASSWORD, 72},
333 {IDC_SPELLCHECK_MULTI_LINGUAL, 73},
334 {IDC_CONTENT_CONTEXT_OPEN_WITH1, 74},
335 {IDC_CONTENT_CONTEXT_OPEN_WITH2, 75},
336 {IDC_CONTENT_CONTEXT_OPEN_WITH3, 76},
337 {IDC_CONTENT_CONTEXT_OPEN_WITH4, 77},
338 {IDC_CONTENT_CONTEXT_OPEN_WITH5, 78},
339 {IDC_CONTENT_CONTEXT_OPEN_WITH6, 79},
340 {IDC_CONTENT_CONTEXT_OPEN_WITH7, 80},
341 {IDC_CONTENT_CONTEXT_OPEN_WITH8, 81},
342 {IDC_CONTENT_CONTEXT_OPEN_WITH9, 82},
343 {IDC_CONTENT_CONTEXT_OPEN_WITH10, 83},
344 {IDC_CONTENT_CONTEXT_OPEN_WITH11, 84},
345 {IDC_CONTENT_CONTEXT_OPEN_WITH12, 85},
346 {IDC_CONTENT_CONTEXT_OPEN_WITH13, 86},
347 {IDC_CONTENT_CONTEXT_OPEN_WITH14, 87},
348 {IDC_CONTENT_CONTEXT_EXIT_FULLSCREEN, 88},
349 {IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP, 89},
350 {IDC_CONTENT_CONTEXT_SHOWALLSAVEDPASSWORDS, 90},
351 {IDC_CONTENT_CONTEXT_PICTUREINPICTURE, 91},
352 {IDC_CONTENT_CONTEXT_EMOJI, 92},
353 {IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION1, 93},
354 {IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION2, 94},
355 {IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION3, 95},
356 {IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION4, 96},
357 {IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION5, 97},
358 {IDC_CONTENT_CONTEXT_LOOK_UP, 98},
359 {IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE, 99},
360 {IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE_ONCE, 100},
361 {IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS, 101},
362 {IDC_SEND_TAB_TO_SELF, 102},
363 {IDC_CONTENT_LINK_SEND_TAB_TO_SELF, 103},
364 {IDC_SEND_TAB_TO_SELF_SINGLE_TARGET, 104},
365 {IDC_CONTENT_LINK_SEND_TAB_TO_SELF_SINGLE_TARGET, 105},
366 {IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE, 106},
367 {IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES, 107},
368 {IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE, 108},
369 {IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES, 109},
370 {IDC_CONTENT_CONTEXT_GENERATE_QR_CODE, 110},
371 {IDC_CONTENT_CLIPBOARD_HISTORY_MENU, 111},
372 {IDC_CONTENT_CONTEXT_COPYLINKTOTEXT, 112},
373 // To add new items:
374 // - Add one more line above this comment block, using the UMA value
375 // from the line below this comment block.
376 // - Increment the UMA value in that latter line.
377 // - Add the new item to the RenderViewContextMenuItem enum in
378 // tools/metrics/histograms/enums.xml.
379 {0, 113}});
380
381 // These UMA values are for the the ContextMenuOptionDesktop enum, used for
382 // the ContextMenu.SelectedOptionDesktop histograms.
383 static const base::NoDestructor<std::map<int, int>> kSpecificMap(
384 {{IDC_CONTENT_CONTEXT_OPENLINKNEWTAB, 0},
385 {IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD, 1},
386 {IDC_CONTENT_CONTEXT_COPYLINKLOCATION, 2},
387 {IDC_CONTENT_CONTEXT_COPY, 3},
388 {IDC_CONTENT_CONTEXT_SAVELINKAS, 4},
389 {IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 5},
390 {IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB, 6},
391 {IDC_CONTENT_CONTEXT_COPYIMAGE, 7},
392 {IDC_CONTENT_CONTEXT_COPYIMAGELOCATION, 8},
393 {IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE, 9},
394 {IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW, 10},
395 {IDC_PRINT, 11},
396 {IDC_CONTENT_CONTEXT_SEARCHWEBFOR, 12},
397 {IDC_CONTENT_CONTEXT_SAVEAVAS, 13},
398 {IDC_SPELLCHECK_SUGGESTION_0, 14},
399 {IDC_SPELLCHECK_ADD_TO_DICTIONARY, 15},
400 {IDC_CONTENT_CONTEXT_SPELLING_TOGGLE, 16},
401 {IDC_CONTENT_CONTEXT_CUT, 17},
402 {IDC_CONTENT_CONTEXT_PASTE, 18},
403 {IDC_CONTENT_CONTEXT_GOTOURL, 19},
404 {IDC_CONTENT_CONTEXT_COPYLINKTOTEXT, 20},
405 // To add new items:
406 // - Add one more line above this comment block, using the UMA value
407 // from the line below this comment block.
408 // - Increment the UMA value in that latter line.
409 // - Add the new item to the ContextMenuOptionDesktop enum in
410 // tools/metrics/histograms/enums.xml.
411 {0, 21}});
412
413 return *(type == UmaEnumIdLookupType::GeneralEnumId ? kGeneralMap
414 : kSpecificMap);
415 }
416
GetUmaValueMax(UmaEnumIdLookupType type)417 int GetUmaValueMax(UmaEnumIdLookupType type) {
418 // The IDC_ "value" of 0 is really a sentinel for the UMA max value.
419 return GetIdcToUmaMap(type).find(0)->second;
420 }
421
422 // Collapses large ranges of ids before looking for UMA enum.
CollapseCommandsForUMA(int id)423 int CollapseCommandsForUMA(int id) {
424 DCHECK(!RenderViewContextMenu::IsContentCustomCommandId(id));
425 DCHECK(!ContextMenuMatcher::IsExtensionsCustomCommandId(id));
426
427 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
428 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
429 return IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
430 }
431
432 if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
433 id <= IDC_SPELLCHECK_LANGUAGES_LAST) {
434 return IDC_SPELLCHECK_LANGUAGES_FIRST;
435 }
436
437 if (id >= IDC_SPELLCHECK_SUGGESTION_0 &&
438 id <= IDC_SPELLCHECK_SUGGESTION_LAST) {
439 return IDC_SPELLCHECK_SUGGESTION_0;
440 }
441
442 if (id >= IDC_OPEN_LINK_IN_PROFILE_FIRST &&
443 id <= IDC_OPEN_LINK_IN_PROFILE_LAST) {
444 return IDC_OPEN_LINK_IN_PROFILE_FIRST;
445 }
446
447 return id;
448 }
449
450 // Returns UMA enum value for command specified by |id| or -1 if not found.
FindUMAEnumValueForCommand(int id,UmaEnumIdLookupType type)451 int FindUMAEnumValueForCommand(int id, UmaEnumIdLookupType type) {
452 if (RenderViewContextMenu::IsContentCustomCommandId(id))
453 return 0;
454
455 if (ContextMenuMatcher::IsExtensionsCustomCommandId(id))
456 return 1;
457
458 id = CollapseCommandsForUMA(id);
459 const auto& map = GetIdcToUmaMap(type);
460 auto it = map.find(id);
461 if (it == map.end())
462 return -1;
463
464 return it->second;
465 }
466
467 // Returns true if the command id is for opening a link.
IsCommandForOpenLink(int id)468 bool IsCommandForOpenLink(int id) {
469 return id == IDC_CONTENT_CONTEXT_OPENLINKNEWTAB ||
470 id == IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW ||
471 id == IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD ||
472 (id >= IDC_OPEN_LINK_IN_PROFILE_FIRST &&
473 id <= IDC_OPEN_LINK_IN_PROFILE_LAST);
474 }
475
476 // Returns the preference of the profile represented by the |context|.
GetPrefs(content::BrowserContext * context)477 PrefService* GetPrefs(content::BrowserContext* context) {
478 return user_prefs::UserPrefs::Get(context);
479 }
480
ExtensionPatternMatch(const extensions::URLPatternSet & patterns,const GURL & url)481 bool ExtensionPatternMatch(const extensions::URLPatternSet& patterns,
482 const GURL& url) {
483 // No patterns means no restriction, so that implicitly matches.
484 if (patterns.is_empty())
485 return true;
486 return patterns.MatchesURL(url);
487 }
488
GetDocumentURL(const content::ContextMenuParams & params)489 const GURL& GetDocumentURL(const content::ContextMenuParams& params) {
490 return params.frame_url.is_empty() ? params.page_url : params.frame_url;
491 }
492
CreateReferrer(const GURL & url,const content::ContextMenuParams & params)493 content::Referrer CreateReferrer(const GURL& url,
494 const content::ContextMenuParams& params) {
495 const GURL& referring_url = GetDocumentURL(params);
496 return content::Referrer::SanitizeForRequest(
497 url,
498 content::Referrer(referring_url.GetAsReferrer(), params.referrer_policy));
499 }
500
GetWebContentsToUse(content::WebContents * web_contents)501 content::WebContents* GetWebContentsToUse(content::WebContents* web_contents) {
502 #if BUILDFLAG(ENABLE_EXTENSIONS)
503 // If we're viewing in a MimeHandlerViewGuest, use its embedder WebContents.
504 auto* guest_view =
505 extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
506 if (guest_view)
507 return guest_view->embedder_web_contents();
508 #endif
509 return web_contents;
510 }
511
512 bool g_custom_id_ranges_initialized = false;
513
514 #if !defined(OS_CHROMEOS)
AddAvatarToLastMenuItem(const gfx::Image & icon,ui::SimpleMenuModel * menu)515 void AddAvatarToLastMenuItem(const gfx::Image& icon,
516 ui::SimpleMenuModel* menu) {
517 // Don't try to scale too small icons.
518 if (icon.Width() < 16 || icon.Height() < 16)
519 return;
520 int target_dip_width = icon.Width();
521 int target_dip_height = icon.Height();
522 gfx::CalculateFaviconTargetSize(&target_dip_width, &target_dip_height);
523 gfx::Image sized_icon = profiles::GetSizedAvatarIcon(
524 icon, true /* is_rectangle */, target_dip_width, target_dip_height,
525 profiles::SHAPE_CIRCLE);
526 menu->SetIcon(menu->GetItemCount() - 1,
527 ui::ImageModel::FromImage(sized_icon));
528 }
529 #endif // !defined(OS_CHROMEOS)
530
OnProfileCreated(const GURL & link_url,const content::Referrer & referrer,Profile * profile,Profile::CreateStatus status)531 void OnProfileCreated(const GURL& link_url,
532 const content::Referrer& referrer,
533 Profile* profile,
534 Profile::CreateStatus status) {
535 if (status == Profile::CREATE_STATUS_INITIALIZED) {
536 Browser* browser = chrome::FindLastActiveWithProfile(profile);
537 NavigateParams nav_params(
538 browser, link_url,
539 /* |ui::PAGE_TRANSITION_TYPED| is used rather than
540 |ui::PAGE_TRANSITION_LINK| since this ultimately opens the link in
541 another browser. This parameter is used within the tab strip model of
542 the browser it opens in implying a link from the active tab in the
543 destination browser which is not correct. */
544 ui::PAGE_TRANSITION_TYPED);
545 nav_params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
546 nav_params.referrer = referrer;
547 nav_params.window_action = NavigateParams::SHOW_WINDOW;
548 Navigate(&nav_params);
549 }
550 }
551
DoesInputFieldTypeSupportEmoji(blink::ContextMenuDataInputFieldType text_input_type)552 bool DoesInputFieldTypeSupportEmoji(
553 blink::ContextMenuDataInputFieldType text_input_type) {
554 // Disable emoji for input types that definitely do not support emoji.
555 switch (text_input_type) {
556 case blink::ContextMenuDataInputFieldType::kNumber:
557 case blink::ContextMenuDataInputFieldType::kTelephone:
558 case blink::ContextMenuDataInputFieldType::kOther:
559 return false;
560 default:
561 return true;
562 }
563 }
564
565 } // namespace
566
567 // static
IsDevToolsURL(const GURL & url)568 bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) {
569 return url.SchemeIs(content::kChromeDevToolsScheme);
570 }
571
572 // static
AddSpellCheckServiceItem(ui::SimpleMenuModel * menu,bool is_checked)573 void RenderViewContextMenu::AddSpellCheckServiceItem(ui::SimpleMenuModel* menu,
574 bool is_checked) {
575 if (is_checked) {
576 menu->AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_SPELLING_TOGGLE,
577 IDS_CONTENT_CONTEXT_SPELLING_ASK_GOOGLE);
578 } else {
579 menu->AddItemWithStringId(IDC_CONTENT_CONTEXT_SPELLING_TOGGLE,
580 IDS_CONTENT_CONTEXT_SPELLING_ASK_GOOGLE);
581 }
582 }
583
RenderViewContextMenu(content::RenderFrameHost * render_frame_host,const content::ContextMenuParams & params)584 RenderViewContextMenu::RenderViewContextMenu(
585 content::RenderFrameHost* render_frame_host,
586 const content::ContextMenuParams& params)
587 : RenderViewContextMenuBase(render_frame_host, params),
588 extension_items_(browser_context_,
589 this,
590 &menu_model_,
591 base::Bind(MenuItemMatchesParams, params_)),
592 profile_link_submenu_model_(this),
593 multiple_profiles_open_(false),
594 protocol_handler_submenu_model_(this),
595 protocol_handler_registry_(
596 ProtocolHandlerRegistryFactory::GetForBrowserContext(GetProfile())),
597 accessibility_labels_submenu_model_(this),
598 embedder_web_contents_(GetWebContentsToUse(source_web_contents_)) {
599 if (!g_custom_id_ranges_initialized) {
600 g_custom_id_ranges_initialized = true;
601 SetContentCustomCommandIdRange(IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
602 IDC_CONTENT_CONTEXT_CUSTOM_LAST);
603 }
604 set_content_type(
605 ContextMenuContentTypeFactory::Create(source_web_contents_, params));
606 }
607
608 RenderViewContextMenu::~RenderViewContextMenu() = default;
609
610 // Menu construction functions -------------------------------------------------
611
612 #if BUILDFLAG(ENABLE_EXTENSIONS)
613 // static
ExtensionContextAndPatternMatch(const content::ContextMenuParams & params,const MenuItem::ContextList & contexts,const extensions::URLPatternSet & target_url_patterns)614 bool RenderViewContextMenu::ExtensionContextAndPatternMatch(
615 const content::ContextMenuParams& params,
616 const MenuItem::ContextList& contexts,
617 const extensions::URLPatternSet& target_url_patterns) {
618 const bool has_link = !params.link_url.is_empty();
619 const bool has_selection = !params.selection_text.empty();
620 const bool in_frame = !params.frame_url.is_empty();
621
622 if (contexts.Contains(MenuItem::ALL) ||
623 (has_selection && contexts.Contains(MenuItem::SELECTION)) ||
624 (params.is_editable && contexts.Contains(MenuItem::EDITABLE)) ||
625 (in_frame && contexts.Contains(MenuItem::FRAME)))
626 return true;
627
628 if (has_link && contexts.Contains(MenuItem::LINK) &&
629 ExtensionPatternMatch(target_url_patterns, params.link_url))
630 return true;
631
632 switch (params.media_type) {
633 case ContextMenuDataMediaType::kImage:
634 if (contexts.Contains(MenuItem::IMAGE) &&
635 ExtensionPatternMatch(target_url_patterns, params.src_url))
636 return true;
637 break;
638
639 case ContextMenuDataMediaType::kVideo:
640 if (contexts.Contains(MenuItem::VIDEO) &&
641 ExtensionPatternMatch(target_url_patterns, params.src_url))
642 return true;
643 break;
644
645 case ContextMenuDataMediaType::kAudio:
646 if (contexts.Contains(MenuItem::AUDIO) &&
647 ExtensionPatternMatch(target_url_patterns, params.src_url))
648 return true;
649 break;
650
651 default:
652 break;
653 }
654
655 // PAGE is the least specific context, so we only examine that if none of the
656 // other contexts apply (except for FRAME, which is included in PAGE for
657 // backwards compatibility).
658 if (!has_link && !has_selection && !params.is_editable &&
659 params.media_type == ContextMenuDataMediaType::kNone &&
660 contexts.Contains(MenuItem::PAGE))
661 return true;
662
663 return false;
664 }
665
666 // static
MenuItemMatchesParams(const content::ContextMenuParams & params,const extensions::MenuItem * item)667 bool RenderViewContextMenu::MenuItemMatchesParams(
668 const content::ContextMenuParams& params,
669 const extensions::MenuItem* item) {
670 bool match = ExtensionContextAndPatternMatch(params, item->contexts(),
671 item->target_url_patterns());
672 if (!match)
673 return false;
674
675 const GURL& document_url = GetDocumentURL(params);
676 return ExtensionPatternMatch(item->document_url_patterns(), document_url);
677 }
678
AppendAllExtensionItems()679 void RenderViewContextMenu::AppendAllExtensionItems() {
680 extension_items_.Clear();
681 extensions::ExtensionRegistry* registry =
682 extensions::ExtensionRegistry::Get(browser_context_);
683
684 MenuManager* menu_manager = MenuManager::Get(browser_context_);
685 if (!menu_manager)
686 return;
687
688 base::string16 printable_selection_text = PrintableSelectionText();
689 EscapeAmpersands(&printable_selection_text);
690
691 // Get a list of extension id's that have context menu items, and sort by the
692 // top level context menu title of the extension.
693 std::vector<base::string16> sorted_menu_titles;
694 std::map<base::string16, std::vector<const Extension*>>
695 title_to_extensions_map;
696 for (const auto& id : menu_manager->ExtensionIds()) {
697 const Extension* extension = registry->GetExtensionById(
698 id.extension_id, extensions::ExtensionRegistry::ENABLED);
699 // Platform apps have their context menus created directly in
700 // AppendPlatformAppItems.
701 if (extension && !extension->is_platform_app()) {
702 base::string16 menu_title = extension_items_.GetTopLevelContextMenuTitle(
703 id, printable_selection_text);
704 title_to_extensions_map[menu_title].push_back(extension);
705 sorted_menu_titles.push_back(menu_title);
706 }
707 }
708 if (sorted_menu_titles.empty())
709 return;
710
711 const std::string app_locale = g_browser_process->GetApplicationLocale();
712 l10n_util::SortStrings16(app_locale, &sorted_menu_titles);
713 sorted_menu_titles.erase(
714 std::unique(sorted_menu_titles.begin(), sorted_menu_titles.end()),
715 sorted_menu_titles.end());
716
717 int index = 0;
718 for (const auto& title : sorted_menu_titles) {
719 const std::vector<const Extension*>& extensions =
720 title_to_extensions_map[title];
721 for (const Extension* extension : extensions) {
722 MenuItem::ExtensionKey extension_key(extension->id());
723 extension_items_.AppendExtensionItems(extension_key,
724 printable_selection_text, &index,
725 /*is_action_menu=*/false);
726 }
727 }
728 }
729
AppendCurrentExtensionItems()730 void RenderViewContextMenu::AppendCurrentExtensionItems() {
731 // Avoid appending extension related items when |extension| is null.
732 // For Panel, this happens when the panel is navigated to a url outside of the
733 // extension's package.
734 const Extension* extension = GetExtension();
735 if (!extension)
736 return;
737
738 extensions::WebViewGuest* web_view_guest =
739 extensions::WebViewGuest::FromWebContents(source_web_contents_);
740 MenuItem::ExtensionKey key;
741 if (web_view_guest) {
742 key = MenuItem::ExtensionKey(extension->id(),
743 web_view_guest->owner_web_contents()
744 ->GetMainFrame()
745 ->GetProcess()
746 ->GetID(),
747 web_view_guest->view_instance_id());
748 } else {
749 key = MenuItem::ExtensionKey(extension->id());
750 }
751
752 // Only add extension items from this extension.
753 int index = 0;
754 extension_items_.AppendExtensionItems(key, PrintableSelectionText(), &index,
755 /*is_action_menu=*/false);
756 }
757 #endif // BUILDFLAG(ENABLE_EXTENSIONS)
758
FormatURLForClipboard(const GURL & url)759 base::string16 RenderViewContextMenu::FormatURLForClipboard(const GURL& url) {
760 DCHECK(!url.is_empty());
761 DCHECK(url.is_valid());
762
763 GURL url_to_format = url;
764 url_formatter::FormatUrlTypes format_types;
765 net::UnescapeRule::Type unescape_rules;
766 if (url.SchemeIs(url::kMailToScheme)) {
767 GURL::Replacements replacements;
768 replacements.ClearQuery();
769 url_to_format = url.ReplaceComponents(replacements);
770 format_types = url_formatter::kFormatUrlOmitMailToScheme;
771 unescape_rules =
772 net::UnescapeRule::PATH_SEPARATORS |
773 net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS;
774 } else {
775 format_types = url_formatter::kFormatUrlOmitNothing;
776 unescape_rules = net::UnescapeRule::NONE;
777 }
778
779 return url_formatter::FormatUrl(url_to_format, format_types, unescape_rules,
780 nullptr, nullptr, nullptr);
781 }
782
WriteURLToClipboard(const GURL & url)783 void RenderViewContextMenu::WriteURLToClipboard(const GURL& url) {
784 if (url.is_empty() || !url.is_valid())
785 return;
786
787 ui::ScopedClipboardWriter scw(
788 ui::ClipboardBuffer::kCopyPaste,
789 CreateDataEndpoint(/*notify_if_restricted=*/true));
790 scw.WriteText(FormatURLForClipboard(url));
791 }
792
InitMenu()793 void RenderViewContextMenu::InitMenu() {
794 RenderViewContextMenuBase::InitMenu();
795
796 if (content_type_->SupportsGroup(
797 ContextMenuContentType::ITEM_GROUP_PASSWORD)) {
798 AppendPasswordItems();
799 }
800
801 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PAGE))
802 AppendPageItems();
803
804 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_LINK)) {
805 AppendLinkItems();
806 if (params_.media_type != ContextMenuDataMediaType::kNone)
807 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
808 }
809
810 bool media_image = content_type_->SupportsGroup(
811 ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE);
812 if (media_image)
813 AppendImageItems();
814
815 if (content_type_->SupportsGroup(
816 ContextMenuContentType::ITEM_GROUP_SEARCHWEBFORIMAGE)) {
817 AppendSearchWebForImageItems();
818 }
819
820 if (content_type_->SupportsGroup(
821 ContextMenuContentType::ITEM_GROUP_MEDIA_VIDEO)) {
822 AppendVideoItems();
823 }
824
825 if (content_type_->SupportsGroup(
826 ContextMenuContentType::ITEM_GROUP_MEDIA_AUDIO)) {
827 AppendAudioItems();
828 }
829
830 if (content_type_->SupportsGroup(
831 ContextMenuContentType::ITEM_GROUP_MEDIA_CANVAS)) {
832 AppendCanvasItems();
833 }
834
835 if (content_type_->SupportsGroup(
836 ContextMenuContentType::ITEM_GROUP_MEDIA_PLUGIN)) {
837 AppendPluginItems();
838 }
839
840 // ITEM_GROUP_MEDIA_FILE has no specific items.
841
842 bool editable =
843 content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_EDITABLE);
844 if (editable)
845 AppendEditableItems();
846
847 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_COPY)) {
848 DCHECK(!editable);
849 AppendCopyItem();
850
851 if (base::FeatureList::IsEnabled(features::kCopyLinkToText)) {
852 AppendCopyLinkToTextItem();
853 }
854 }
855
856 if (!content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_LINK))
857 AppendSharingItems();
858
859 if (content_type_->SupportsGroup(
860 ContextMenuContentType::ITEM_GROUP_SEARCH_PROVIDER) &&
861 params_.misspelled_word.empty()) {
862 AppendSearchProvider();
863 }
864
865 if (!media_image &&
866 content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PRINT)) {
867 AppendPrintItem();
868 }
869
870 // Spell check and writing direction options are not currently supported by
871 // pepper plugins.
872 if (editable && params_.misspelled_word.empty() &&
873 !content_type_->SupportsGroup(
874 ContextMenuContentType::ITEM_GROUP_MEDIA_PLUGIN)) {
875 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
876 AppendLanguageSettings();
877 AppendPlatformEditableItems();
878 }
879
880 if (content_type_->SupportsGroup(
881 ContextMenuContentType::ITEM_GROUP_MEDIA_PLUGIN)) {
882 AppendRotationItems();
883 }
884
885 bool supports_smart_text_selection = false;
886 #if defined(OS_CHROMEOS)
887 supports_smart_text_selection =
888 content_type_->SupportsGroup(
889 ContextMenuContentType::ITEM_GROUP_SMART_SELECTION) &&
890 arc::IsArcPlayStoreEnabledForProfile(GetProfile());
891 #endif // defined(OS_CHROMEOS)
892 if (supports_smart_text_selection)
893 AppendSmartSelectionActionItems();
894
895 extension_items_.set_smart_text_selection_enabled(
896 supports_smart_text_selection);
897
898 if (content_type_->SupportsGroup(
899 ContextMenuContentType::ITEM_GROUP_ALL_EXTENSION)) {
900 DCHECK(!content_type_->SupportsGroup(
901 ContextMenuContentType::ITEM_GROUP_CURRENT_EXTENSION));
902 AppendAllExtensionItems();
903 }
904
905 if (content_type_->SupportsGroup(
906 ContextMenuContentType::ITEM_GROUP_CURRENT_EXTENSION)) {
907 DCHECK(!content_type_->SupportsGroup(
908 ContextMenuContentType::ITEM_GROUP_ALL_EXTENSION));
909 AppendCurrentExtensionItems();
910 }
911
912 // Accessibility label items are appended to all menus when a screen reader
913 // is enabled. It can be difficult to open a specific context menu with a
914 // screen reader, so this is a UX approved solution.
915 bool added_accessibility_labels_items = AppendAccessibilityLabelsItems();
916
917 if (content_type_->SupportsGroup(
918 ContextMenuContentType::ITEM_GROUP_DEVELOPER)) {
919 AppendDeveloperItems();
920 }
921
922 if (content_type_->SupportsGroup(
923 ContextMenuContentType::ITEM_GROUP_DEVTOOLS_UNPACKED_EXT)) {
924 AppendDevtoolsForUnpackedExtensions();
925 }
926
927 if (content_type_->SupportsGroup(
928 ContextMenuContentType::ITEM_GROUP_PRINT_PREVIEW)) {
929 AppendPrintPreviewItems();
930 }
931
932 // Remove any redundant trailing separator.
933 int index = menu_model_.GetItemCount() - 1;
934 if (index >= 0 &&
935 menu_model_.GetTypeAt(index) == ui::MenuModel::TYPE_SEPARATOR) {
936 menu_model_.RemoveItemAt(index);
937 }
938
939 // If there is only one item and it is the Accessibility labels item, remove
940 // it. We only show this item when it is not the only item.
941 // Note that the separator added in AppendAccessibilityLabelsItems will not
942 // actually be added if this is the first item in the list, so we don't need
943 // to check for or remove the initial separator.
944 if (added_accessibility_labels_items && menu_model_.GetItemCount() == 1) {
945 menu_model_.RemoveItemAt(0);
946 }
947
948 // Always add Quick Answers view last, as it is rendered next to the context
949 // menu, meaning that each menu item added/removed in this function will cause
950 // it to visibly jump on the screen (see b/173569669).
951 AppendQuickAnswersItems();
952 }
953
GetProfile() const954 Profile* RenderViewContextMenu::GetProfile() const {
955 return Profile::FromBrowserContext(browser_context_);
956 }
957
RecordUsedItem(int id)958 void RenderViewContextMenu::RecordUsedItem(int id) {
959 // Log general ID.
960
961 int enum_id =
962 FindUMAEnumValueForCommand(id, UmaEnumIdLookupType::GeneralEnumId);
963 if (enum_id == -1) {
964 NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id;
965 return;
966 }
967
968 UMA_HISTOGRAM_EXACT_LINEAR(
969 "RenderViewContextMenu.Used", enum_id,
970 GetUmaValueMax(UmaEnumIdLookupType::GeneralEnumId));
971
972 // Log other situations.
973
974 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_LINK) &&
975 // Ignore link-related commands that don't actually open a link.
976 IsCommandForOpenLink(id) &&
977 // Ignore using right click + open in new tab for internal links.
978 !params_.link_url.SchemeIs(content::kChromeUIScheme)) {
979 const GURL doc_url = GetDocumentURL(params_);
980 const GURL history_url = GURL(chrome::kChromeUIHistoryURL);
981 if (doc_url == history_url.Resolve(chrome::kChromeUIHistorySyncedTabs)) {
982 UMA_HISTOGRAM_ENUMERATION(
983 "HistoryPage.OtherDevicesMenu",
984 browser_sync::SyncedTabsHistogram::OPENED_LINK_VIA_CONTEXT_MENU,
985 browser_sync::SyncedTabsHistogram::LIMIT);
986 } else if (doc_url == GURL(chrome::kChromeUIDownloadsURL)) {
987 base::RecordAction(base::UserMetricsAction(
988 "Downloads_OpenUrlOfDownloadedItemFromContextMenu"));
989 } else if (doc_url == GURL(chrome::kChromeSearchLocalNtpUrl)) {
990 base::RecordAction(
991 base::UserMetricsAction("NTP_LinkOpenedFromContextMenu"));
992 } else if (doc_url.GetOrigin() == chrome::kChromeSearchMostVisitedUrl) {
993 base::RecordAction(
994 base::UserMetricsAction("MostVisited_ClickedFromContextMenu"));
995 } else if (doc_url.GetOrigin() == GURL(chrome::kChromeUINewTabPageURL) ||
996 doc_url.GetOrigin() ==
997 GURL(chrome::kChromeUIUntrustedNewTabPageUrl)) {
998 base::RecordAction(base::UserMetricsAction(
999 "NewTabPage.LinkOpenedFromContextMenu.WebUI"));
1000 }
1001 }
1002
1003 // Log for specific contexts. Note that since the menu is displayed for
1004 // contexts (all of the ContextMenuContentType::SupportsXXX() functions),
1005 // these UMA metrics are necessarily best-effort in distilling into a context.
1006
1007 enum_id = FindUMAEnumValueForCommand(
1008 id, UmaEnumIdLookupType::ContextSpecificEnumId);
1009 if (enum_id == -1)
1010 return;
1011
1012 if (content_type_->SupportsGroup(
1013 ContextMenuContentType::ITEM_GROUP_MEDIA_VIDEO)) {
1014 UMA_HISTOGRAM_EXACT_LINEAR(
1015 "ContextMenu.SelectedOptionDesktop.Video", enum_id,
1016 GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
1017 } else if (content_type_->SupportsGroup(
1018 ContextMenuContentType::ITEM_GROUP_LINK) &&
1019 content_type_->SupportsGroup(
1020 ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE)) {
1021 UMA_HISTOGRAM_EXACT_LINEAR(
1022 "ContextMenu.SelectedOptionDesktop.ImageLink", enum_id,
1023 GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
1024 } else if (content_type_->SupportsGroup(
1025 ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE)) {
1026 UMA_HISTOGRAM_EXACT_LINEAR(
1027 "ContextMenu.SelectedOptionDesktop.Image", enum_id,
1028 GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
1029 } else if (!params_.misspelled_word.empty()) {
1030 UMA_HISTOGRAM_EXACT_LINEAR(
1031 "ContextMenu.SelectedOptionDesktop.MisspelledWord", enum_id,
1032 GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
1033 } else if (!params_.selection_text.empty() &&
1034 params_.media_type == ContextMenuDataMediaType::kNone) {
1035 // Probably just text.
1036 UMA_HISTOGRAM_EXACT_LINEAR(
1037 "ContextMenu.SelectedOptionDesktop.SelectedText", enum_id,
1038 GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
1039 } else {
1040 UMA_HISTOGRAM_EXACT_LINEAR(
1041 "ContextMenu.SelectedOptionDesktop.Other", enum_id,
1042 GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
1043 }
1044 }
1045
RecordShownItem(int id)1046 void RenderViewContextMenu::RecordShownItem(int id) {
1047 int enum_id =
1048 FindUMAEnumValueForCommand(id, UmaEnumIdLookupType::GeneralEnumId);
1049 if (enum_id != -1) {
1050 UMA_HISTOGRAM_EXACT_LINEAR(
1051 "RenderViewContextMenu.Shown", enum_id,
1052 GetUmaValueMax(UmaEnumIdLookupType::GeneralEnumId));
1053 } else {
1054 // Just warning here. It's harder to maintain list of all possibly
1055 // visible items than executable items.
1056 DLOG(ERROR) << "Update kUmaEnumToControlId. Unhanded IDC: " << id;
1057 }
1058 }
1059
IsHTML5Fullscreen() const1060 bool RenderViewContextMenu::IsHTML5Fullscreen() const {
1061 Browser* browser = chrome::FindBrowserWithWebContents(source_web_contents_);
1062 if (!browser)
1063 return false;
1064
1065 FullscreenController* controller =
1066 browser->exclusive_access_manager()->fullscreen_controller();
1067 return controller->IsTabFullscreen();
1068 }
1069
IsPressAndHoldEscRequiredToExitFullscreen() const1070 bool RenderViewContextMenu::IsPressAndHoldEscRequiredToExitFullscreen() const {
1071 Browser* browser = chrome::FindBrowserWithWebContents(source_web_contents_);
1072 if (!browser)
1073 return false;
1074
1075 KeyboardLockController* controller =
1076 browser->exclusive_access_manager()->keyboard_lock_controller();
1077 return controller->RequiresPressAndHoldEscToExit();
1078 }
1079
1080 #if BUILDFLAG(ENABLE_PLUGINS)
HandleAuthorizeAllPlugins()1081 void RenderViewContextMenu::HandleAuthorizeAllPlugins() {
1082 ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins(
1083 source_web_contents_, false, std::string());
1084 }
1085 #endif
1086
AppendPrintPreviewItems()1087 void RenderViewContextMenu::AppendPrintPreviewItems() {
1088 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
1089 if (!print_preview_menu_observer_) {
1090 print_preview_menu_observer_ =
1091 std::make_unique<PrintPreviewContextMenuObserver>(source_web_contents_);
1092 }
1093
1094 observers_.AddObserver(print_preview_menu_observer_.get());
1095 #endif
1096 }
1097
GetExtension() const1098 const Extension* RenderViewContextMenu::GetExtension() const {
1099 return extensions::ProcessManager::Get(browser_context_)
1100 ->GetExtensionForWebContents(source_web_contents_);
1101 }
1102
GetTargetLanguage() const1103 std::string RenderViewContextMenu::GetTargetLanguage() const {
1104 std::unique_ptr<translate::TranslatePrefs> prefs(
1105 ChromeTranslateClient::CreateTranslatePrefs(GetPrefs(browser_context_)));
1106 language::LanguageModel* language_model =
1107 LanguageModelManagerFactory::GetForBrowserContext(browser_context_)
1108 ->GetPrimaryModel();
1109 return translate::TranslateManager::GetTargetLanguage(prefs.get(),
1110 language_model);
1111 }
1112
AppendDeveloperItems()1113 void RenderViewContextMenu::AppendDeveloperItems() {
1114 // Do not Show Inspect Element for DevTools.
1115 bool show_developer_items = !IsDevToolsURL(params_.page_url);
1116
1117 if (!show_developer_items)
1118 return;
1119
1120 // In the DevTools popup menu, "developer items" is normally the only
1121 // section, so omit the separator there.
1122 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1123 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PAGE))
1124 menu_model_.AddItemWithStringId(IDC_VIEW_SOURCE,
1125 IDS_CONTENT_CONTEXT_VIEWPAGESOURCE);
1126 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_FRAME)) {
1127 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE,
1128 IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE);
1129 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOADFRAME,
1130 IDS_CONTENT_CONTEXT_RELOADFRAME);
1131 }
1132 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTELEMENT,
1133 IDS_CONTENT_CONTEXT_INSPECTELEMENT);
1134 }
1135
AppendDevtoolsForUnpackedExtensions()1136 void RenderViewContextMenu::AppendDevtoolsForUnpackedExtensions() {
1137 // Add a separator if there are any items already in the menu.
1138 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1139
1140 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP,
1141 IDS_CONTENT_CONTEXT_RELOAD_PACKAGED_APP);
1142 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP,
1143 IDS_CONTENT_CONTEXT_RESTART_APP);
1144 AppendDeveloperItems();
1145 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE,
1146 IDS_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE);
1147 }
1148
AppendLinkItems()1149 void RenderViewContextMenu::AppendLinkItems() {
1150 if (!params_.link_url.is_empty()) {
1151 const Browser* browser = GetBrowser();
1152 const bool in_app = browser && browser->deprecated_is_app();
1153 WebContents* active_web_contents =
1154 browser ? browser->tab_strip_model()->GetActiveWebContents() : nullptr;
1155
1156 menu_model_.AddItemWithStringId(
1157 IDC_CONTENT_CONTEXT_OPENLINKNEWTAB,
1158 in_app ? IDS_CONTENT_CONTEXT_OPENLINKNEWTAB_INAPP
1159 : IDS_CONTENT_CONTEXT_OPENLINKNEWTAB);
1160 if (!in_app) {
1161 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW,
1162 IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW);
1163 }
1164
1165 if (params_.link_url.is_valid()) {
1166 AppendProtocolHandlerSubMenu();
1167 }
1168
1169 menu_model_.AddItemWithStringId(
1170 IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD,
1171 in_app ? IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP
1172 : IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD);
1173
1174 AppendOpenInWebAppLinkItems();
1175 AppendOpenWithLinkItems();
1176
1177 // While ChromeOS supports multiple profiles, only one can be open at a
1178 // time.
1179 // TODO(jochen): Consider adding support for ChromeOS with similar
1180 // semantics as the profile switcher in the system tray.
1181 #if !defined(OS_CHROMEOS)
1182 // g_browser_process->profile_manager() is null during unit tests.
1183 if (g_browser_process->profile_manager() &&
1184 GetProfile()->IsRegularProfile()) {
1185 ProfileManager* profile_manager = g_browser_process->profile_manager();
1186 // Find all regular profiles other than the current one which have at
1187 // least one open window.
1188 std::vector<ProfileAttributesEntry*> entries =
1189 profile_manager->GetProfileAttributesStorage()
1190 .GetAllProfilesAttributesSortedByName();
1191 std::vector<ProfileAttributesEntry*> target_profiles_entries;
1192 for (ProfileAttributesEntry* entry : entries) {
1193 base::FilePath profile_path = entry->GetPath();
1194 Profile* profile = profile_manager->GetProfileByPath(profile_path);
1195 if (profile != GetProfile() && !entry->IsOmitted() &&
1196 !entry->IsSigninRequired()) {
1197 target_profiles_entries.push_back(entry);
1198 if (chrome::FindLastActiveWithProfile(profile))
1199 multiple_profiles_open_ = true;
1200 }
1201 }
1202
1203 if (multiple_profiles_open_ && !target_profiles_entries.empty()) {
1204 if (target_profiles_entries.size() == 1) {
1205 int menu_index = static_cast<int>(profile_link_paths_.size());
1206 ProfileAttributesEntry* entry = target_profiles_entries.front();
1207 profile_link_paths_.push_back(entry->GetPath());
1208 menu_model_.AddItem(
1209 IDC_OPEN_LINK_IN_PROFILE_FIRST + menu_index,
1210 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_OPENLINKINPROFILE,
1211 entry->GetName()));
1212 AddAvatarToLastMenuItem(entry->GetAvatarIcon(), &menu_model_);
1213 } else {
1214 for (ProfileAttributesEntry* entry : target_profiles_entries) {
1215 int menu_index = static_cast<int>(profile_link_paths_.size());
1216 // In extreme cases, we might have more profiles than available
1217 // command ids. In that case, just stop creating new entries - the
1218 // menu is probably useless at this point already.
1219 if (IDC_OPEN_LINK_IN_PROFILE_FIRST + menu_index >
1220 IDC_OPEN_LINK_IN_PROFILE_LAST) {
1221 break;
1222 }
1223 profile_link_paths_.push_back(entry->GetPath());
1224 profile_link_submenu_model_.AddItem(
1225 IDC_OPEN_LINK_IN_PROFILE_FIRST + menu_index, entry->GetName());
1226 AddAvatarToLastMenuItem(entry->GetAvatarIcon(),
1227 &profile_link_submenu_model_);
1228 }
1229 menu_model_.AddSubMenuWithStringId(
1230 IDC_CONTENT_CONTEXT_OPENLINKINPROFILE,
1231 IDS_CONTENT_CONTEXT_OPENLINKINPROFILES,
1232 &profile_link_submenu_model_);
1233 }
1234 }
1235 }
1236 #endif // !defined(OS_CHROMEOS)
1237
1238 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1239
1240 // Place QR Generator close to send-tab-to-self feature for link images.
1241 if (params_.has_image_contents)
1242 AppendQRCodeGeneratorItem(/*for_image=*/true, /*draw_icon=*/true);
1243
1244 if (browser && send_tab_to_self::ShouldOfferFeatureForLink(
1245 active_web_contents, params_.link_url)) {
1246 if (send_tab_to_self::GetValidDeviceCount(GetBrowser()->profile()) == 1) {
1247 #if defined(OS_MAC)
1248 menu_model_.AddItem(IDC_CONTENT_LINK_SEND_TAB_TO_SELF_SINGLE_TARGET,
1249 l10n_util::GetStringFUTF16(
1250 IDS_LINK_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET,
1251 send_tab_to_self::GetSingleTargetDeviceName(
1252 GetBrowser()->profile())));
1253 #else
1254 menu_model_.AddItemWithIcon(
1255 IDC_CONTENT_LINK_SEND_TAB_TO_SELF_SINGLE_TARGET,
1256 l10n_util::GetStringFUTF16(
1257 IDS_LINK_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET,
1258 send_tab_to_self::GetSingleTargetDeviceName(
1259 GetBrowser()->profile())),
1260 ui::ImageModel::FromVectorIcon(kSendTabToSelfIcon));
1261 #endif
1262 } else {
1263 send_tab_to_self_sub_menu_model_ =
1264 std::make_unique<send_tab_to_self::SendTabToSelfSubMenuModel>(
1265 active_web_contents,
1266 send_tab_to_self::SendTabToSelfMenuType::kLink,
1267 params_.link_url);
1268 #if defined(OS_MAC)
1269 menu_model_.AddSubMenuWithStringId(
1270 IDC_CONTENT_LINK_SEND_TAB_TO_SELF, IDS_LINK_MENU_SEND_TAB_TO_SELF,
1271 send_tab_to_self_sub_menu_model_.get());
1272 #else
1273 menu_model_.AddSubMenuWithStringIdAndIcon(
1274 IDC_CONTENT_LINK_SEND_TAB_TO_SELF, IDS_LINK_MENU_SEND_TAB_TO_SELF,
1275 send_tab_to_self_sub_menu_model_.get(),
1276 ui::ImageModel::FromVectorIcon(kSendTabToSelfIcon));
1277 #endif
1278 }
1279 }
1280
1281 AppendClickToCallItem();
1282
1283 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1284 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVELINKAS,
1285 IDS_CONTENT_CONTEXT_SAVELINKAS);
1286 }
1287
1288 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYLINKLOCATION,
1289 params_.link_url.SchemeIs(url::kMailToScheme)
1290 ? IDS_CONTENT_CONTEXT_COPYEMAILADDRESS
1291 : IDS_CONTENT_CONTEXT_COPYLINKLOCATION);
1292
1293 if (params_.source_type == ui::MENU_SOURCE_TOUCH &&
1294 params_.media_type != ContextMenuDataMediaType::kImage &&
1295 !params_.link_text.empty()) {
1296 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYLINKTEXT,
1297 IDS_CONTENT_CONTEXT_COPYLINKTEXT);
1298 }
1299 }
1300
AppendOpenWithLinkItems()1301 void RenderViewContextMenu::AppendOpenWithLinkItems() {
1302 #if defined(OS_CHROMEOS)
1303 open_with_menu_observer_ =
1304 std::make_unique<arc::OpenWithMenu>(browser_context_, this);
1305 observers_.AddObserver(open_with_menu_observer_.get());
1306 open_with_menu_observer_->InitMenu(params_);
1307 #endif
1308 }
1309
AppendQuickAnswersItems()1310 void RenderViewContextMenu::AppendQuickAnswersItems() {
1311 #if defined(OS_CHROMEOS)
1312 if (!quick_answers_menu_observer_) {
1313 quick_answers_menu_observer_ =
1314 std::make_unique<QuickAnswersMenuObserver>(this);
1315 }
1316
1317 observers_.AddObserver(quick_answers_menu_observer_.get());
1318 quick_answers_menu_observer_->InitMenu(params_);
1319 #endif
1320 }
1321
AppendSmartSelectionActionItems()1322 void RenderViewContextMenu::AppendSmartSelectionActionItems() {
1323 #if defined(OS_CHROMEOS)
1324 start_smart_selection_action_menu_observer_ =
1325 std::make_unique<arc::StartSmartSelectionActionMenu>(this);
1326 observers_.AddObserver(start_smart_selection_action_menu_observer_.get());
1327
1328 if (menu_model_.GetItemCount())
1329 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1330 start_smart_selection_action_menu_observer_->InitMenu(params_);
1331 #endif
1332 }
1333
AppendOpenInWebAppLinkItems()1334 void RenderViewContextMenu::AppendOpenInWebAppLinkItems() {
1335 Profile* const profile = Profile::FromBrowserContext(browser_context_);
1336 if (!apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile))
1337 return;
1338
1339 base::Optional<web_app::AppId> app_id =
1340 web_app::FindInstalledAppWithUrlInScope(profile, params_.link_url);
1341 if (!app_id)
1342 return;
1343
1344 int open_in_app_string_id;
1345 const Browser* browser = GetBrowser();
1346 if (browser && browser->app_name() ==
1347 web_app::GenerateApplicationNameFromAppId(*app_id)) {
1348 open_in_app_string_id = IDS_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP_SAMEAPP;
1349 } else {
1350 open_in_app_string_id = IDS_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP;
1351 }
1352
1353 auto* const provider = web_app::WebAppProviderBase::GetProviderBase(profile);
1354 menu_model_.AddItem(
1355 IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP,
1356 l10n_util::GetStringFUTF16(
1357 open_in_app_string_id,
1358 base::UTF8ToUTF16(provider->registrar().GetAppShortName(*app_id))));
1359
1360 gfx::Image icon = gfx::Image::CreateFrom1xBitmap(
1361 provider->icon_manager().GetFavicon(*app_id));
1362 menu_model_.SetIcon(menu_model_.GetItemCount() - 1,
1363 ui::ImageModel::FromImage(icon));
1364 }
1365
AppendImageItems()1366 void RenderViewContextMenu::AppendImageItems() {
1367 if (!params_.has_image_contents) {
1368 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_LOAD_IMAGE,
1369 IDS_CONTENT_CONTEXT_LOAD_IMAGE);
1370 }
1371 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB,
1372 IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB);
1373 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS,
1374 IDS_CONTENT_CONTEXT_SAVEIMAGEAS);
1375 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGE,
1376 IDS_CONTENT_CONTEXT_COPYIMAGE);
1377 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION,
1378 IDS_CONTENT_CONTEXT_COPYIMAGELOCATION);
1379
1380 // Don't double-add for linked images, which also add the item.
1381 if (params_.link_url.is_empty())
1382 AppendQRCodeGeneratorItem(/*for_image=*/true, /*draw_icon=*/false);
1383 }
1384
AppendSearchWebForImageItems()1385 void RenderViewContextMenu::AppendSearchWebForImageItems() {
1386 if (!params_.has_image_contents)
1387 return;
1388
1389 TemplateURLService* service =
1390 TemplateURLServiceFactory::GetForProfile(GetProfile());
1391 const TemplateURL* const provider = service->GetDefaultSearchProvider();
1392 if (!provider || provider->image_url().empty() ||
1393 !provider->image_url_ref().IsValid(service->search_terms_data())) {
1394 return;
1395 }
1396
1397 menu_model_.AddItem(
1398 IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
1399 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
1400 provider->short_name()));
1401 }
1402
AppendAudioItems()1403 void RenderViewContextMenu::AppendAudioItems() {
1404 AppendMediaItems();
1405 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1406 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB,
1407 IDS_CONTENT_CONTEXT_OPENAUDIONEWTAB);
1408 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
1409 IDS_CONTENT_CONTEXT_SAVEAUDIOAS);
1410 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION,
1411 IDS_CONTENT_CONTEXT_COPYAUDIOLOCATION);
1412 AppendMediaRouterItem();
1413 }
1414
AppendCanvasItems()1415 void RenderViewContextMenu::AppendCanvasItems() {
1416 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS,
1417 IDS_CONTENT_CONTEXT_SAVEIMAGEAS);
1418 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGE,
1419 IDS_CONTENT_CONTEXT_COPYIMAGE);
1420 }
1421
AppendVideoItems()1422 void RenderViewContextMenu::AppendVideoItems() {
1423 AppendMediaItems();
1424 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1425 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB,
1426 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB);
1427 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
1428 IDS_CONTENT_CONTEXT_SAVEVIDEOAS);
1429 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION,
1430 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION);
1431 AppendPictureInPictureItem();
1432 AppendMediaRouterItem();
1433 }
1434
AppendMediaItems()1435 void RenderViewContextMenu::AppendMediaItems() {
1436 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_LOOP,
1437 IDS_CONTENT_CONTEXT_LOOP);
1438 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_CONTROLS,
1439 IDS_CONTENT_CONTEXT_CONTROLS);
1440 }
1441
AppendPluginItems()1442 void RenderViewContextMenu::AppendPluginItems() {
1443 if (params_.page_url == params_.src_url ||
1444 (guest_view::GuestViewBase::IsGuest(source_web_contents_) &&
1445 (!embedder_web_contents_ || !embedder_web_contents_->IsSavable()))) {
1446 // Both full page and embedded plugins are hosted as guest now,
1447 // the difference is a full page plugin is not considered as savable.
1448 // For full page plugin, we show page menu items so long as focus is not
1449 // within an editable text area.
1450 if (params_.link_url.is_empty() && params_.selection_text.empty() &&
1451 !params_.is_editable) {
1452 AppendPageItems();
1453 }
1454 } else {
1455 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
1456 IDS_CONTENT_CONTEXT_SAVEPAGEAS);
1457 // The "Print" menu item should always be included for plugins. If
1458 // content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PRINT)
1459 // is true the item will be added inside AppendPrintItem(). Otherwise we
1460 // add "Print" here.
1461 if (!content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PRINT))
1462 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
1463 }
1464 }
1465
AppendPageItems()1466 void RenderViewContextMenu::AppendPageItems() {
1467 AppendExitFullscreenItem();
1468
1469 menu_model_.AddItemWithStringId(IDC_BACK, IDS_CONTENT_CONTEXT_BACK);
1470 menu_model_.AddItemWithStringId(IDC_FORWARD, IDS_CONTENT_CONTEXT_FORWARD);
1471 menu_model_.AddItemWithStringId(IDC_RELOAD, IDS_CONTENT_CONTEXT_RELOAD);
1472 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1473 menu_model_.AddItemWithStringId(IDC_SAVE_PAGE,
1474 IDS_CONTENT_CONTEXT_SAVEPAGEAS);
1475 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
1476 AppendMediaRouterItem();
1477
1478 // Send-Tab-To-Self (user's other devices), page level.
1479 bool send_tab_to_self_menu_present = false;
1480 if (GetBrowser() &&
1481 send_tab_to_self::ShouldOfferFeature(
1482 GetBrowser()->tab_strip_model()->GetActiveWebContents())) {
1483 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1484 send_tab_to_self_menu_present = true;
1485 if (send_tab_to_self::GetValidDeviceCount(GetBrowser()->profile()) == 1) {
1486 #if defined(OS_MAC)
1487 menu_model_.AddItem(IDC_SEND_TAB_TO_SELF_SINGLE_TARGET,
1488 l10n_util::GetStringFUTF16(
1489 IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET,
1490 send_tab_to_self::GetSingleTargetDeviceName(
1491 GetBrowser()->profile())));
1492 #else
1493 menu_model_.AddItemWithIcon(
1494 IDC_SEND_TAB_TO_SELF_SINGLE_TARGET,
1495 l10n_util::GetStringFUTF16(
1496 IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET,
1497 send_tab_to_self::GetSingleTargetDeviceName(
1498 GetBrowser()->profile())),
1499 ui::ImageModel::FromVectorIcon(kSendTabToSelfIcon));
1500 #endif
1501 } else {
1502 send_tab_to_self_sub_menu_model_ =
1503 std::make_unique<send_tab_to_self::SendTabToSelfSubMenuModel>(
1504 GetBrowser()->tab_strip_model()->GetActiveWebContents(),
1505 send_tab_to_self::SendTabToSelfMenuType::kContent);
1506 #if defined(OS_MAC)
1507 menu_model_.AddSubMenuWithStringId(
1508 IDC_SEND_TAB_TO_SELF, IDS_CONTEXT_MENU_SEND_TAB_TO_SELF,
1509 send_tab_to_self_sub_menu_model_.get());
1510 #else
1511 menu_model_.AddSubMenuWithStringIdAndIcon(
1512 IDC_SEND_TAB_TO_SELF, IDS_CONTEXT_MENU_SEND_TAB_TO_SELF,
1513 send_tab_to_self_sub_menu_model_.get(),
1514 ui::ImageModel::FromVectorIcon(kSendTabToSelfIcon));
1515 #endif
1516 }
1517 }
1518
1519 // Context menu item for QR Code Generator.
1520 if (IsQRCodeGeneratorEnabled()) {
1521 // This is presented alongside the send-tab-to-self items, though each may
1522 // be present without the other due to feature experimentation. Therefore we
1523 // may or may not need to create a new separator.
1524 if (!send_tab_to_self_menu_present)
1525 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1526
1527 AppendQRCodeGeneratorItem(/*for_image=*/false, /*draw_icon=*/true);
1528
1529 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1530 } else if (send_tab_to_self_menu_present) {
1531 // Close out sharing section if send-tab-to-self was present but QR
1532 // generator was not.
1533 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1534 }
1535
1536 ChromeTranslateClient* chrome_translate_client =
1537 ChromeTranslateClient::FromWebContents(embedder_web_contents_);
1538 const bool canTranslate =
1539 chrome_translate_client &&
1540 chrome_translate_client->GetTranslateManager()->CanManuallyTranslate(
1541 true);
1542 if (canTranslate) {
1543 language::LanguageModel* language_model =
1544 LanguageModelManagerFactory::GetForBrowserContext(browser_context_)
1545 ->GetPrimaryModel();
1546 std::unique_ptr<translate::TranslatePrefs> prefs(
1547 ChromeTranslateClient::CreateTranslatePrefs(
1548 GetPrefs(browser_context_)));
1549 std::string locale = translate::TranslateManager::GetTargetLanguage(
1550 prefs.get(), language_model);
1551 base::string16 language =
1552 l10n_util::GetDisplayNameForLocale(locale, locale, true);
1553 menu_model_.AddItem(
1554 IDC_CONTENT_CONTEXT_TRANSLATE,
1555 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE, language));
1556 }
1557 }
1558
AppendExitFullscreenItem()1559 void RenderViewContextMenu::AppendExitFullscreenItem() {
1560 Browser* browser = GetBrowser();
1561 if (!browser)
1562 return;
1563
1564 // Only show item if in fullscreen mode.
1565 if (!browser->exclusive_access_manager()
1566 ->fullscreen_controller()
1567 ->IsControllerInitiatedFullscreen()) {
1568 return;
1569 }
1570
1571 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_EXIT_FULLSCREEN,
1572 IDS_CONTENT_CONTEXT_EXIT_FULLSCREEN);
1573 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1574 }
1575
AppendCopyItem()1576 void RenderViewContextMenu::AppendCopyItem() {
1577 if (menu_model_.GetItemCount())
1578 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1579 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY,
1580 IDS_CONTENT_CONTEXT_COPY);
1581 }
1582
AppendCopyLinkToTextItem()1583 void RenderViewContextMenu::AppendCopyLinkToTextItem() {
1584 if (copy_link_to_text_menu_observer_)
1585 return;
1586
1587 copy_link_to_text_menu_observer_ = CopyLinkToTextMenuObserver::Create(this);
1588 if (copy_link_to_text_menu_observer_) {
1589 observers_.AddObserver(copy_link_to_text_menu_observer_.get());
1590 copy_link_to_text_menu_observer_->InitMenu(params_);
1591 }
1592 }
1593
AppendPrintItem()1594 void RenderViewContextMenu::AppendPrintItem() {
1595 if (GetPrefs(browser_context_)->GetBoolean(prefs::kPrintingEnabled) &&
1596 (params_.media_type == ContextMenuDataMediaType::kNone ||
1597 params_.media_flags & WebContextMenuData::kMediaCanPrint) &&
1598 params_.misspelled_word.empty()) {
1599 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
1600 }
1601 }
1602
AppendMediaRouterItem()1603 void RenderViewContextMenu::AppendMediaRouterItem() {
1604 if (media_router::MediaRouterEnabled(browser_context_)) {
1605 menu_model_.AddItemWithStringId(IDC_ROUTE_MEDIA,
1606 IDS_MEDIA_ROUTER_MENU_ITEM_TITLE);
1607 }
1608 }
1609
AppendRotationItems()1610 void RenderViewContextMenu::AppendRotationItems() {
1611 if (params_.media_flags & WebContextMenuData::kMediaCanRotate) {
1612 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1613 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECW,
1614 IDS_CONTENT_CONTEXT_ROTATECW);
1615 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECCW,
1616 IDS_CONTENT_CONTEXT_ROTATECCW);
1617 }
1618 }
1619
AppendSearchProvider()1620 void RenderViewContextMenu::AppendSearchProvider() {
1621 DCHECK(browser_context_);
1622
1623 base::TrimWhitespace(params_.selection_text, base::TRIM_ALL,
1624 ¶ms_.selection_text);
1625 if (params_.selection_text.empty())
1626 return;
1627
1628 base::ReplaceChars(params_.selection_text, AutocompleteMatch::kInvalidChars,
1629 base::ASCIIToUTF16(" "), ¶ms_.selection_text);
1630
1631 AutocompleteMatch match;
1632 AutocompleteClassifierFactory::GetForProfile(GetProfile())
1633 ->Classify(params_.selection_text, false, false,
1634 metrics::OmniboxEventProto::INVALID_SPEC, &match, nullptr);
1635 selection_navigation_url_ = match.destination_url;
1636 if (!selection_navigation_url_.is_valid())
1637 return;
1638
1639 base::string16 printable_selection_text = PrintableSelectionText();
1640 EscapeAmpersands(&printable_selection_text);
1641
1642 if (AutocompleteMatch::IsSearchType(match.type)) {
1643 const TemplateURL* const default_provider =
1644 TemplateURLServiceFactory::GetForProfile(GetProfile())
1645 ->GetDefaultSearchProvider();
1646 if (!default_provider)
1647 return;
1648
1649 if (!base::Contains(
1650 params_.properties,
1651 prefs::kDefaultSearchProviderContextMenuAccessAllowed)) {
1652 return;
1653 }
1654
1655 menu_model_.AddItem(
1656 IDC_CONTENT_CONTEXT_SEARCHWEBFOR,
1657 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR,
1658 default_provider->short_name(),
1659 printable_selection_text));
1660 } else {
1661 if ((selection_navigation_url_ != params_.link_url) &&
1662 ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1663 selection_navigation_url_.scheme())) {
1664 menu_model_.AddItem(
1665 IDC_CONTENT_CONTEXT_GOTOURL,
1666 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_GOTOURL,
1667 printable_selection_text));
1668 }
1669 }
1670 }
1671
AppendEditableItems()1672 void RenderViewContextMenu::AppendEditableItems() {
1673 const bool use_spelling = !chrome::IsRunningInForcedAppMode();
1674 if (use_spelling)
1675 AppendSpellingSuggestionItems();
1676
1677 if (!params_.misspelled_word.empty()) {
1678 AppendSearchProvider();
1679 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1680 }
1681 if (params_.misspelled_word.empty() &&
1682 DoesInputFieldTypeSupportEmoji(params_.input_field_type) &&
1683 ui::IsEmojiPanelSupported()) {
1684 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_EMOJI,
1685 IDS_CONTENT_CONTEXT_EMOJI);
1686 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1687 }
1688
1689 // 'Undo' and 'Redo' for text input with no suggestions and no text selected.
1690 // We make an exception for OS X as context clicking will select the closest
1691 // word. In this case both items are always shown.
1692 #if defined(OS_MAC)
1693 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO,
1694 IDS_CONTENT_CONTEXT_UNDO);
1695 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO,
1696 IDS_CONTENT_CONTEXT_REDO);
1697 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1698 #else
1699 // Also want to show 'Undo' and 'Redo' if 'Emoji' is the only item in the menu
1700 // so far.
1701 if (!IsDevToolsURL(params_.page_url) &&
1702 !content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PRINT) &&
1703 (!menu_model_.GetItemCount() ||
1704 menu_model_.GetIndexOfCommandId(IDC_CONTENT_CONTEXT_EMOJI) != -1)) {
1705 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO,
1706 IDS_CONTENT_CONTEXT_UNDO);
1707 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO,
1708 IDS_CONTENT_CONTEXT_REDO);
1709 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1710 }
1711 #endif
1712
1713 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_CUT,
1714 IDS_CONTENT_CONTEXT_CUT);
1715 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY,
1716 IDS_CONTENT_CONTEXT_COPY);
1717 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE,
1718 IDS_CONTENT_CONTEXT_PASTE);
1719
1720 const bool has_misspelled_word = !params_.misspelled_word.empty();
1721 if (!has_misspelled_word) {
1722 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE,
1723 IDS_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE);
1724 }
1725
1726 #if defined(OS_CHROMEOS)
1727 if (chromeos::features::IsClipboardHistoryEnabled()) {
1728 menu_model_.AddItemWithStringId(
1729 IDC_CONTENT_CLIPBOARD_HISTORY_MENU,
1730 IDS_CONTEXT_MENU_SHOW_CLIPBOARD_HISTORY_MENU);
1731 }
1732 #endif
1733
1734 if (!has_misspelled_word) {
1735 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SELECTALL,
1736 IDS_CONTENT_CONTEXT_SELECTALL);
1737 }
1738
1739 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1740 }
1741
AppendLanguageSettings()1742 void RenderViewContextMenu::AppendLanguageSettings() {
1743 const bool use_spelling = !chrome::IsRunningInForcedAppMode();
1744 if (!use_spelling)
1745 return;
1746
1747 #if defined(OS_MAC)
1748 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS,
1749 IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS);
1750 #else
1751 if (!spelling_options_submenu_observer_) {
1752 const int kLanguageRadioGroup = 1;
1753 spelling_options_submenu_observer_ =
1754 std::make_unique<SpellingOptionsSubMenuObserver>(this, this,
1755 kLanguageRadioGroup);
1756 }
1757
1758 spelling_options_submenu_observer_->InitMenu(params_);
1759 observers_.AddObserver(spelling_options_submenu_observer_.get());
1760 #endif
1761 }
1762
AppendSpellingSuggestionItems()1763 void RenderViewContextMenu::AppendSpellingSuggestionItems() {
1764 if (!spelling_suggestions_menu_observer_) {
1765 spelling_suggestions_menu_observer_ =
1766 std::make_unique<SpellingMenuObserver>(this);
1767 }
1768 observers_.AddObserver(spelling_suggestions_menu_observer_.get());
1769 spelling_suggestions_menu_observer_->InitMenu(params_);
1770 }
1771
AppendAccessibilityLabelsItems()1772 bool RenderViewContextMenu::AppendAccessibilityLabelsItems() {
1773 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1774 if (!accessibility_labels_menu_observer_) {
1775 accessibility_labels_menu_observer_ =
1776 std::make_unique<AccessibilityLabelsMenuObserver>(this);
1777 }
1778 observers_.AddObserver(accessibility_labels_menu_observer_.get());
1779 accessibility_labels_menu_observer_->InitMenu(params_);
1780 return accessibility_labels_menu_observer_->ShouldShowLabelsItem();
1781 }
1782
AppendProtocolHandlerSubMenu()1783 void RenderViewContextMenu::AppendProtocolHandlerSubMenu() {
1784 const ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1785 GetHandlersForLinkUrl();
1786 if (handlers.empty())
1787 return;
1788 size_t max = IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST -
1789 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
1790 for (size_t i = 0; i < handlers.size() && i <= max; i++) {
1791 protocol_handler_submenu_model_.AddItem(
1792 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST + i,
1793 base::UTF8ToUTF16(handlers[i].url().host()));
1794 }
1795 protocol_handler_submenu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1796 protocol_handler_submenu_model_.AddItem(
1797 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS,
1798 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH_CONFIGURE));
1799
1800 menu_model_.AddSubMenu(
1801 IDC_CONTENT_CONTEXT_OPENLINKWITH,
1802 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH),
1803 &protocol_handler_submenu_model_);
1804 }
1805
AppendPasswordItems()1806 void RenderViewContextMenu::AppendPasswordItems() {
1807 bool add_separator = false;
1808
1809 password_manager::ContentPasswordManagerDriver* driver =
1810 password_manager::ContentPasswordManagerDriver::GetForRenderFrameHost(
1811 GetRenderFrameHost());
1812 // Don't show the item for guest or incognito profiles and also when the
1813 // automatic generation feature is disabled.
1814 if (password_manager_util::ManualPasswordGenerationEnabled(driver)) {
1815 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_GENERATEPASSWORD,
1816 IDS_CONTENT_CONTEXT_GENERATEPASSWORD);
1817 add_separator = true;
1818 }
1819 if (password_manager_util::ShowAllSavedPasswordsContextMenuEnabled(driver)) {
1820 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SHOWALLSAVEDPASSWORDS,
1821 IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK);
1822 add_separator = true;
1823 }
1824
1825 if (add_separator)
1826 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1827 }
1828
AppendPictureInPictureItem()1829 void RenderViewContextMenu::AppendPictureInPictureItem() {
1830 if (base::FeatureList::IsEnabled(media::kPictureInPicture))
1831 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_PICTUREINPICTURE,
1832 IDS_CONTENT_CONTEXT_PICTUREINPICTURE);
1833 }
1834
AppendSharingItems()1835 void RenderViewContextMenu::AppendSharingItems() {
1836 int items_initial = menu_model_.GetItemCount();
1837 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1838 // Check if the starting separator got added.
1839 int items_before_sharing = menu_model_.GetItemCount();
1840 bool starting_separator_added = items_before_sharing > items_initial;
1841
1842 AppendClickToCallItem();
1843 AppendSharedClipboardItem();
1844
1845 // Add an ending separator if there are sharing items, otherwise remove the
1846 // starting separator iff we added one above.
1847 int sharing_items = menu_model_.GetItemCount() - items_before_sharing;
1848 if (sharing_items > 0)
1849 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1850 else if (starting_separator_added)
1851 menu_model_.RemoveItemAt(items_initial);
1852 }
1853
AppendClickToCallItem()1854 void RenderViewContextMenu::AppendClickToCallItem() {
1855 SharingClickToCallEntryPoint entry_point;
1856 base::Optional<std::string> phone_number;
1857 std::string selection_text;
1858 if (ShouldOfferClickToCallForURL(browser_context_, params_.link_url)) {
1859 entry_point = SharingClickToCallEntryPoint::kRightClickLink;
1860 phone_number = GetUnescapedURLContent(params_.link_url);
1861 } else if (!params_.selection_text.empty()) {
1862 entry_point = SharingClickToCallEntryPoint::kRightClickSelection;
1863 selection_text = base::UTF16ToUTF8(params_.selection_text);
1864 phone_number =
1865 ExtractPhoneNumberForClickToCall(browser_context_, selection_text);
1866 }
1867
1868 if (!phone_number || phone_number->empty())
1869 return;
1870
1871 if (!click_to_call_context_menu_observer_) {
1872 click_to_call_context_menu_observer_ =
1873 std::make_unique<ClickToCallContextMenuObserver>(this);
1874 observers_.AddObserver(click_to_call_context_menu_observer_.get());
1875 }
1876
1877 click_to_call_context_menu_observer_->BuildMenu(*phone_number, selection_text,
1878 entry_point);
1879 }
1880
AppendSharedClipboardItem()1881 void RenderViewContextMenu::AppendSharedClipboardItem() {
1882 if (!ShouldOfferSharedClipboard(browser_context_, params_.selection_text))
1883 return;
1884
1885 if (!shared_clipboard_context_menu_observer_) {
1886 shared_clipboard_context_menu_observer_ =
1887 std::make_unique<SharedClipboardContextMenuObserver>(this);
1888 observers_.AddObserver(shared_clipboard_context_menu_observer_.get());
1889 }
1890 shared_clipboard_context_menu_observer_->InitMenu(params_);
1891 }
1892
1893 // Menu delegate functions -----------------------------------------------------
1894
IsCommandIdEnabled(int id) const1895 bool RenderViewContextMenu::IsCommandIdEnabled(int id) const {
1896 // Disable context menu in locked fullscreen mode (the menu is not really
1897 // disabled as the user can still open it, but all the individual context menu
1898 // entries are disabled / greyed out).
1899 if (GetBrowser() && platform_util::IsBrowserLockedFullscreen(GetBrowser()))
1900 return false;
1901
1902 {
1903 bool enabled = false;
1904 if (RenderViewContextMenuBase::IsCommandIdKnown(id, &enabled))
1905 return enabled;
1906 }
1907
1908 CoreTabHelper* core_tab_helper =
1909 CoreTabHelper::FromWebContents(source_web_contents_);
1910 int content_restrictions = 0;
1911 if (core_tab_helper)
1912 content_restrictions = core_tab_helper->content_restrictions();
1913 if (id == IDC_PRINT && (content_restrictions & CONTENT_RESTRICTION_PRINT))
1914 return false;
1915
1916 if (id == IDC_SAVE_PAGE &&
1917 (content_restrictions & CONTENT_RESTRICTION_SAVE)) {
1918 return false;
1919 }
1920
1921 PrefService* prefs = GetPrefs(browser_context_);
1922
1923 // Allow Spell Check language items on sub menu for text area context menu.
1924 if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) &&
1925 (id < IDC_SPELLCHECK_LANGUAGES_LAST)) {
1926 return prefs->GetBoolean(spellcheck::prefs::kSpellCheckEnable);
1927 }
1928
1929 // Extension items.
1930 if (ContextMenuMatcher::IsExtensionsCustomCommandId(id))
1931 return extension_items_.IsCommandIdEnabled(id);
1932
1933 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
1934 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
1935 return true;
1936 }
1937
1938 if (id >= IDC_OPEN_LINK_IN_PROFILE_FIRST &&
1939 id <= IDC_OPEN_LINK_IN_PROFILE_LAST) {
1940 return params_.link_url.is_valid();
1941 }
1942
1943 switch (id) {
1944 case IDC_BACK:
1945 return embedder_web_contents_->GetController().CanGoBack();
1946
1947 case IDC_FORWARD:
1948 return embedder_web_contents_->GetController().CanGoForward();
1949
1950 case IDC_RELOAD:
1951 return IsReloadEnabled();
1952
1953 case IDC_VIEW_SOURCE:
1954 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
1955 return IsViewSourceEnabled();
1956
1957 case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
1958 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE:
1959 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP:
1960 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP:
1961 return IsDevCommandEnabled(id);
1962
1963 case IDC_CONTENT_CONTEXT_TRANSLATE:
1964 return IsTranslateEnabled();
1965
1966 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB:
1967 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
1968 case IDC_CONTENT_CONTEXT_OPENLINKINPROFILE:
1969 case IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP:
1970 return params_.link_url.is_valid();
1971
1972 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION:
1973 return params_.unfiltered_link_url.is_valid();
1974
1975 case IDC_CONTENT_CONTEXT_COPYLINKTEXT:
1976 return true;
1977
1978 case IDC_CONTENT_CONTEXT_SAVELINKAS:
1979 return IsSaveLinkAsEnabled();
1980
1981 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS:
1982 return IsSaveImageAsEnabled();
1983
1984 // The images shown in the most visited thumbnails can't be opened or
1985 // searched for conventionally.
1986 case IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB:
1987 case IDC_CONTENT_CONTEXT_LOAD_IMAGE:
1988 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
1989 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
1990 return params_.src_url.is_valid() &&
1991 (params_.src_url.scheme() != content::kChromeUIScheme);
1992
1993 case IDC_CONTENT_CONTEXT_COPYIMAGE:
1994 return params_.has_image_contents;
1995
1996 // Media control commands should all be disabled if the player is in an
1997 // error state.
1998 case IDC_CONTENT_CONTEXT_PLAYPAUSE:
1999 return (params_.media_flags & WebContextMenuData::kMediaInError) == 0;
2000
2001 // Loop command should be disabled if the player is in an error state.
2002 case IDC_CONTENT_CONTEXT_LOOP:
2003 return (params_.media_flags & WebContextMenuData::kMediaCanLoop) != 0 &&
2004 (params_.media_flags & WebContextMenuData::kMediaInError) == 0;
2005
2006 // Mute and unmute should also be disabled if the player has no audio.
2007 case IDC_CONTENT_CONTEXT_MUTE:
2008 return (params_.media_flags & WebContextMenuData::kMediaHasAudio) != 0 &&
2009 (params_.media_flags & WebContextMenuData::kMediaInError) == 0;
2010
2011 case IDC_CONTENT_CONTEXT_CONTROLS:
2012 return (params_.media_flags &
2013 WebContextMenuData::kMediaCanToggleControls) != 0;
2014
2015 case IDC_CONTENT_CONTEXT_ROTATECW:
2016 case IDC_CONTENT_CONTEXT_ROTATECCW:
2017 return (params_.media_flags & WebContextMenuData::kMediaCanRotate) != 0;
2018
2019 case IDC_CONTENT_CONTEXT_COPYAVLOCATION:
2020 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION:
2021 return params_.src_url.is_valid();
2022
2023 case IDC_CONTENT_CONTEXT_SAVEAVAS:
2024 return IsSaveAsEnabled();
2025
2026 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
2027 // Currently, a media element can be opened in a new tab iff it can
2028 // be saved. So rather than duplicating the MediaCanSave flag, we rely
2029 // on that here.
2030 return !!(params_.media_flags & WebContextMenuData::kMediaCanSave);
2031
2032 case IDC_SAVE_PAGE:
2033 return IsSavePageEnabled();
2034
2035 case IDC_CONTENT_CONTEXT_RELOADFRAME:
2036 return params_.frame_url.is_valid() &&
2037 params_.frame_url.GetOrigin() != chrome::kChromeUIPrintURL;
2038
2039 case IDC_CONTENT_CONTEXT_UNDO:
2040 return !!(params_.edit_flags & ContextMenuDataEditFlags::kCanUndo);
2041
2042 case IDC_CONTENT_CONTEXT_REDO:
2043 return !!(params_.edit_flags & ContextMenuDataEditFlags::kCanRedo);
2044
2045 case IDC_CONTENT_CONTEXT_CUT:
2046 return !!(params_.edit_flags & ContextMenuDataEditFlags::kCanCut);
2047
2048 case IDC_CONTENT_CONTEXT_COPY:
2049 return !!(params_.edit_flags & ContextMenuDataEditFlags::kCanCopy);
2050
2051 case IDC_CONTENT_CONTEXT_PASTE:
2052 return IsPasteEnabled();
2053
2054 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE:
2055 return IsPasteAndMatchStyleEnabled();
2056
2057 case IDC_CONTENT_CONTEXT_DELETE:
2058 return !!(params_.edit_flags & ContextMenuDataEditFlags::kCanDelete);
2059
2060 case IDC_CONTENT_CONTEXT_SELECTALL:
2061 return !!(params_.edit_flags & ContextMenuDataEditFlags::kCanSelectAll);
2062
2063 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
2064 return IsOpenLinkOTREnabled();
2065
2066 case IDC_PRINT:
2067 return IsPrintPreviewEnabled();
2068
2069 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR:
2070 case IDC_CONTENT_CONTEXT_GOTOURL:
2071 case IDC_SPELLPANEL_TOGGLE:
2072 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS:
2073 case IDC_SEND_TAB_TO_SELF:
2074 case IDC_SEND_TAB_TO_SELF_SINGLE_TARGET:
2075 return true;
2076
2077 case IDC_CONTENT_CONTEXT_GENERATE_QR_CODE:
2078 return IsQRCodeGeneratorEnabled();
2079
2080 case IDC_CONTENT_LINK_SEND_TAB_TO_SELF:
2081 case IDC_CONTENT_LINK_SEND_TAB_TO_SELF_SINGLE_TARGET:
2082 return send_tab_to_self::AreContentRequirementsMet(
2083 params_.link_url, GetBrowser()->profile());
2084
2085 case IDC_CHECK_SPELLING_WHILE_TYPING:
2086 return prefs->GetBoolean(spellcheck::prefs::kSpellCheckEnable);
2087
2088 #if !defined(OS_MAC) && !defined(OS_BSD) && defined(OS_POSIX)
2089 // TODO(suzhe): this should not be enabled for password fields.
2090 case IDC_INPUT_METHODS_MENU:
2091 return true;
2092 #endif
2093
2094 case IDC_SPELLCHECK_MENU:
2095 case IDC_CONTENT_CONTEXT_OPENLINKWITH:
2096 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS:
2097 case IDC_CONTENT_CONTEXT_GENERATEPASSWORD:
2098 case IDC_CONTENT_CONTEXT_SHOWALLSAVEDPASSWORDS:
2099 return true;
2100
2101 case IDC_ROUTE_MEDIA:
2102 return IsRouteMediaEnabled();
2103
2104 case IDC_CONTENT_CONTEXT_EXIT_FULLSCREEN:
2105 return true;
2106
2107 case IDC_CONTENT_CONTEXT_PICTUREINPICTURE:
2108 return !!(params_.media_flags &
2109 WebContextMenuData::kMediaCanPictureInPicture);
2110
2111 case IDC_CONTENT_CONTEXT_EMOJI:
2112 return params_.is_editable;
2113
2114 case IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION1:
2115 case IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION2:
2116 case IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION3:
2117 case IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION4:
2118 case IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION5:
2119 return true;
2120
2121 case IDC_CONTENT_CLIPBOARD_HISTORY_MENU:
2122 #if defined(OS_CHROMEOS)
2123 if (chromeos::features::IsClipboardHistoryEnabled())
2124 return ash::ClipboardHistoryController::Get()->CanShowMenu();
2125 #else
2126 NOTREACHED();
2127 #endif
2128 return false;
2129
2130 default:
2131 NOTREACHED();
2132 return false;
2133 }
2134 }
2135
IsCommandIdChecked(int id) const2136 bool RenderViewContextMenu::IsCommandIdChecked(int id) const {
2137 if (RenderViewContextMenuBase::IsCommandIdChecked(id))
2138 return true;
2139
2140 // See if the video is set to looping.
2141 if (id == IDC_CONTENT_CONTEXT_LOOP)
2142 return (params_.media_flags & WebContextMenuData::kMediaLoop) != 0;
2143
2144 if (id == IDC_CONTENT_CONTEXT_CONTROLS)
2145 return (params_.media_flags & WebContextMenuData::kMediaControls) != 0;
2146
2147 if (id == IDC_CONTENT_CONTEXT_PICTUREINPICTURE)
2148 return (params_.media_flags & WebContextMenuData::kMediaPictureInPicture) !=
2149 0;
2150
2151 if (id == IDC_CONTENT_CONTEXT_EMOJI)
2152 return false;
2153
2154 // Extension items.
2155 if (ContextMenuMatcher::IsExtensionsCustomCommandId(id))
2156 return extension_items_.IsCommandIdChecked(id);
2157
2158 return false;
2159 }
2160
IsCommandIdVisible(int id) const2161 bool RenderViewContextMenu::IsCommandIdVisible(int id) const {
2162 if (ContextMenuMatcher::IsExtensionsCustomCommandId(id))
2163 return extension_items_.IsCommandIdVisible(id);
2164 return RenderViewContextMenuBase::IsCommandIdVisible(id);
2165 }
2166
ExecuteCommand(int id,int event_flags)2167 void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) {
2168 RenderViewContextMenuBase::ExecuteCommand(id, event_flags);
2169 if (command_executed_)
2170 return;
2171 command_executed_ = true;
2172
2173 // Process extension menu items.
2174 if (ContextMenuMatcher::IsExtensionsCustomCommandId(id)) {
2175 RenderFrameHost* render_frame_host = GetRenderFrameHost();
2176 if (render_frame_host) {
2177 extension_items_.ExecuteCommand(id, source_web_contents_,
2178 render_frame_host, params_);
2179 }
2180 return;
2181 }
2182
2183 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
2184 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
2185 ExecProtocolHandler(event_flags,
2186 id - IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST);
2187 return;
2188 }
2189
2190 if (id >= IDC_OPEN_LINK_IN_PROFILE_FIRST &&
2191 id <= IDC_OPEN_LINK_IN_PROFILE_LAST) {
2192 ExecOpenLinkInProfile(id - IDC_OPEN_LINK_IN_PROFILE_FIRST);
2193 return;
2194 }
2195
2196 switch (id) {
2197 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB:
2198 OpenURLWithExtraHeaders(params_.link_url, GetDocumentURL(params_),
2199 WindowOpenDisposition::NEW_BACKGROUND_TAB,
2200 ui::PAGE_TRANSITION_LINK, "" /* extra_headers */,
2201 true /* started_from_context_menu */);
2202 break;
2203
2204 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
2205 OpenURLWithExtraHeaders(params_.link_url, GetDocumentURL(params_),
2206 WindowOpenDisposition::NEW_WINDOW,
2207 ui::PAGE_TRANSITION_LINK, "" /* extra_headers */,
2208 true /* started_from_context_menu */);
2209 break;
2210
2211 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
2212 OpenURLWithExtraHeaders(params_.link_url, GURL(),
2213 WindowOpenDisposition::OFF_THE_RECORD,
2214 ui::PAGE_TRANSITION_LINK, "" /* extra_headers */,
2215 true /* started_from_context_menu */);
2216 break;
2217
2218 case IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP:
2219 ExecOpenWebApp();
2220 break;
2221
2222 case IDC_CONTENT_CONTEXT_SAVELINKAS:
2223 ExecSaveLinkAs();
2224 break;
2225
2226 case IDC_CONTENT_CONTEXT_SAVEAVAS:
2227 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS:
2228 ExecSaveAs();
2229 break;
2230
2231 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION:
2232 WriteURLToClipboard(params_.unfiltered_link_url);
2233 break;
2234
2235 case IDC_CONTENT_CONTEXT_COPYLINKTEXT:
2236 ExecCopyLinkText();
2237 break;
2238
2239 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION:
2240 case IDC_CONTENT_CONTEXT_COPYAVLOCATION:
2241 WriteURLToClipboard(params_.src_url);
2242 break;
2243
2244 case IDC_CONTENT_CONTEXT_COPYIMAGE:
2245 ExecCopyImageAt();
2246 break;
2247
2248 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
2249 ExecSearchWebForImage();
2250 break;
2251
2252 case IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB:
2253 OpenURLWithExtraHeaders(params_.src_url, GetDocumentURL(params_),
2254 WindowOpenDisposition::NEW_BACKGROUND_TAB,
2255 ui::PAGE_TRANSITION_LINK, std::string(), false);
2256 break;
2257
2258 case IDC_CONTENT_CONTEXT_LOAD_IMAGE:
2259 ExecLoadImage();
2260 break;
2261
2262 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
2263 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
2264 OpenURL(params_.src_url, GetDocumentURL(params_),
2265 WindowOpenDisposition::NEW_BACKGROUND_TAB,
2266 ui::PAGE_TRANSITION_LINK);
2267 break;
2268
2269 case IDC_CONTENT_CONTEXT_PLAYPAUSE:
2270 ExecPlayPause();
2271 break;
2272
2273 case IDC_CONTENT_CONTEXT_MUTE:
2274 ExecMute();
2275 break;
2276
2277 case IDC_CONTENT_CONTEXT_LOOP:
2278 ExecLoop();
2279 break;
2280
2281 case IDC_CONTENT_CONTEXT_CONTROLS:
2282 ExecControls();
2283 break;
2284
2285 case IDC_CONTENT_CONTEXT_ROTATECW:
2286 ExecRotateCW();
2287 break;
2288
2289 case IDC_CONTENT_CONTEXT_ROTATECCW:
2290 ExecRotateCCW();
2291 break;
2292
2293 case IDC_BACK:
2294 embedder_web_contents_->GetController().GoBack();
2295 break;
2296
2297 case IDC_FORWARD:
2298 embedder_web_contents_->GetController().GoForward();
2299 break;
2300
2301 case IDC_SAVE_PAGE:
2302 embedder_web_contents_->OnSavePage();
2303 break;
2304
2305 case IDC_SEND_TAB_TO_SELF_SINGLE_TARGET:
2306 send_tab_to_self::ShareToSingleTarget(
2307 GetBrowser()->tab_strip_model()->GetActiveWebContents());
2308 send_tab_to_self::RecordSendTabToSelfClickResult(
2309 send_tab_to_self::kContentMenu, SendTabToSelfClickResult::kClickItem);
2310 break;
2311
2312 case IDC_CONTENT_LINK_SEND_TAB_TO_SELF_SINGLE_TARGET:
2313 send_tab_to_self::ShareToSingleTarget(
2314 GetBrowser()->tab_strip_model()->GetActiveWebContents(),
2315 params_.link_url);
2316 send_tab_to_self::RecordSendTabToSelfClickResult(
2317 send_tab_to_self::kLinkMenu, SendTabToSelfClickResult::kClickItem);
2318 break;
2319
2320 case IDC_CONTENT_CONTEXT_GENERATE_QR_CODE: {
2321 auto* web_contents =
2322 GetBrowser()->tab_strip_model()->GetActiveWebContents();
2323 auto* bubble_controller =
2324 qrcode_generator::QRCodeGeneratorBubbleController::Get(web_contents);
2325 if (params_.media_type == ContextMenuDataMediaType::kImage) {
2326 base::RecordAction(
2327 UserMetricsAction("SharingQRCode.DialogLaunched.ContextMenuImage"));
2328 bubble_controller->ShowBubble(params_.src_url);
2329 } else {
2330 base::RecordAction(
2331 UserMetricsAction("SharingQRCode.DialogLaunched.ContextMenuPage"));
2332 NavigationEntry* entry =
2333 embedder_web_contents_->GetController().GetLastCommittedEntry();
2334 bubble_controller->ShowBubble(entry->GetURL());
2335 }
2336 break;
2337 }
2338
2339 case IDC_RELOAD:
2340 chrome::Reload(GetBrowser(), WindowOpenDisposition::CURRENT_TAB);
2341 break;
2342
2343 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP:
2344 ExecReloadPackagedApp();
2345 break;
2346
2347 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP:
2348 ExecRestartPackagedApp();
2349 break;
2350
2351 case IDC_PRINT:
2352 ExecPrint();
2353 break;
2354
2355 case IDC_ROUTE_MEDIA:
2356 ExecRouteMedia();
2357 break;
2358
2359 case IDC_CONTENT_CONTEXT_EXIT_FULLSCREEN:
2360 ExecExitFullscreen();
2361 break;
2362
2363 case IDC_VIEW_SOURCE:
2364 embedder_web_contents_->GetMainFrame()->ViewSource();
2365 break;
2366
2367 case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
2368 ExecInspectElement();
2369 break;
2370
2371 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE:
2372 ExecInspectBackgroundPage();
2373 break;
2374
2375 case IDC_CONTENT_CONTEXT_TRANSLATE:
2376 ExecTranslate();
2377 break;
2378
2379 case IDC_CONTENT_CONTEXT_RELOADFRAME:
2380 source_web_contents_->ReloadFocusedFrame();
2381 break;
2382
2383 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
2384 if (GetRenderFrameHost())
2385 GetRenderFrameHost()->ViewSource();
2386 break;
2387
2388 case IDC_CONTENT_CONTEXT_UNDO:
2389 source_web_contents_->Undo();
2390 break;
2391
2392 case IDC_CONTENT_CONTEXT_REDO:
2393 source_web_contents_->Redo();
2394 break;
2395
2396 case IDC_CONTENT_CONTEXT_CUT:
2397 source_web_contents_->Cut();
2398 break;
2399
2400 case IDC_CONTENT_CONTEXT_COPY:
2401 source_web_contents_->Copy();
2402 break;
2403
2404 case IDC_CONTENT_CONTEXT_PASTE:
2405 source_web_contents_->Paste();
2406 break;
2407
2408 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE:
2409 source_web_contents_->PasteAndMatchStyle();
2410 break;
2411
2412 case IDC_CONTENT_CONTEXT_DELETE:
2413 source_web_contents_->Delete();
2414 break;
2415
2416 case IDC_CONTENT_CONTEXT_SELECTALL:
2417 source_web_contents_->SelectAll();
2418 break;
2419
2420 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR:
2421 case IDC_CONTENT_CONTEXT_GOTOURL:
2422 OpenURL(selection_navigation_url_, GURL(),
2423 ui::DispositionFromEventFlags(
2424 event_flags, WindowOpenDisposition::NEW_FOREGROUND_TAB),
2425 ui::PAGE_TRANSITION_LINK);
2426 break;
2427
2428 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS:
2429 ExecLanguageSettings(event_flags);
2430 break;
2431
2432 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS:
2433 ExecProtocolHandlerSettings(event_flags);
2434 break;
2435
2436 case IDC_CONTENT_CONTEXT_GENERATEPASSWORD:
2437 password_manager_util::UserTriggeredManualGenerationFromContextMenu(
2438 ChromePasswordManagerClient::FromWebContents(source_web_contents_));
2439 break;
2440
2441 case IDC_CONTENT_CONTEXT_SHOWALLSAVEDPASSWORDS:
2442 NavigateToManagePasswordsPage(
2443 GetBrowser(),
2444 password_manager::ManagePasswordsReferrer::kPasswordContextMenu);
2445 break;
2446
2447 case IDC_CONTENT_CONTEXT_PICTUREINPICTURE:
2448 ExecPictureInPicture();
2449 break;
2450
2451 case IDC_CONTENT_CONTEXT_EMOJI: {
2452 Browser* browser = GetBrowser();
2453 if (browser) {
2454 browser->window()->ShowEmojiPanel();
2455 } else {
2456 // TODO(https://crbug.com/919167): Ensure this is called in the correct
2457 // process. This fails in print preview for PWA windows on Mac.
2458 ui::ShowEmojiPanel();
2459 }
2460 break;
2461 }
2462
2463 case IDC_CONTENT_CLIPBOARD_HISTORY_MENU: {
2464 #if defined(OS_CHROMEOS)
2465 // Calculate the anchor point in screen coordinates.
2466 gfx::Point anchor_point_in_screen =
2467 GetRenderFrameHost()->GetNativeView()->GetBoundsInScreen().origin();
2468 anchor_point_in_screen.Offset(params_.x, params_.y);
2469
2470 // Calculate the menu source type from `event_flags`.
2471 ui::MenuSourceType source_type;
2472 if (event_flags & ui::EF_LEFT_MOUSE_BUTTON)
2473 source_type = ui::MENU_SOURCE_MOUSE;
2474 else if (event_flags & ui::EF_FROM_TOUCH)
2475 source_type = ui::MENU_SOURCE_TOUCH;
2476 else
2477 source_type = ui::MENU_SOURCE_KEYBOARD;
2478
2479 ash::ClipboardHistoryController::Get()->ShowMenu(
2480 gfx::Rect(anchor_point_in_screen, gfx::Size()),
2481 views::MenuAnchorPosition::kTopLeft, source_type);
2482 #else
2483 NOTREACHED();
2484 #endif
2485 break;
2486 }
2487
2488 default:
2489 NOTREACHED();
2490 break;
2491 }
2492 }
2493
AddSpellCheckServiceItem(bool is_checked)2494 void RenderViewContextMenu::AddSpellCheckServiceItem(bool is_checked) {
2495 AddSpellCheckServiceItem(&menu_model_, is_checked);
2496 }
2497
AddAccessibilityLabelsServiceItem(bool is_checked)2498 void RenderViewContextMenu::AddAccessibilityLabelsServiceItem(bool is_checked) {
2499 if (is_checked) {
2500 menu_model_.AddCheckItemWithStringId(
2501 IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE,
2502 IDS_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_MENU_OPTION);
2503 } else {
2504 // Add the submenu if the whole feature is not enabled.
2505 accessibility_labels_submenu_model_.AddItemWithStringId(
2506 IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE,
2507 IDS_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_SEND);
2508 accessibility_labels_submenu_model_.AddItemWithStringId(
2509 IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE_ONCE,
2510 IDS_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_SEND_ONCE);
2511 menu_model_.AddSubMenu(
2512 IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS,
2513 l10n_util::GetStringUTF16(
2514 IDS_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_MENU_OPTION),
2515 &accessibility_labels_submenu_model_);
2516 }
2517 }
2518
2519 // static
RegisterMenuShownCallbackForTesting(base::OnceCallback<void (RenderViewContextMenu *)> cb)2520 void RenderViewContextMenu::RegisterMenuShownCallbackForTesting(
2521 base::OnceCallback<void(RenderViewContextMenu*)> cb) {
2522 *GetMenuShownCallback() = std::move(cb);
2523 }
2524
2525 ProtocolHandlerRegistry::ProtocolHandlerList
GetHandlersForLinkUrl()2526 RenderViewContextMenu::GetHandlersForLinkUrl() {
2527 ProtocolHandlerRegistry::ProtocolHandlerList handlers =
2528 protocol_handler_registry_->GetHandlersFor(params_.link_url.scheme());
2529 std::sort(handlers.begin(), handlers.end());
2530 return handlers;
2531 }
2532
NotifyMenuShown()2533 void RenderViewContextMenu::NotifyMenuShown() {
2534 auto* cb = GetMenuShownCallback();
2535 if (!cb->is_null())
2536 std::move(*cb).Run(this);
2537 }
2538
PrintableSelectionText()2539 base::string16 RenderViewContextMenu::PrintableSelectionText() {
2540 return gfx::TruncateString(params_.selection_text, kMaxSelectionTextLength,
2541 gfx::WORD_BREAK);
2542 }
2543
EscapeAmpersands(base::string16 * text)2544 void RenderViewContextMenu::EscapeAmpersands(base::string16* text) {
2545 base::ReplaceChars(*text, base::ASCIIToUTF16("&"), base::ASCIIToUTF16("&&"),
2546 text);
2547 }
2548
2549 // Controller functions --------------------------------------------------------
2550
IsReloadEnabled() const2551 bool RenderViewContextMenu::IsReloadEnabled() const {
2552 CoreTabHelper* core_tab_helper =
2553 CoreTabHelper::FromWebContents(embedder_web_contents_);
2554 if (!core_tab_helper)
2555 return false;
2556
2557 Browser* browser = GetBrowser();
2558 return !browser || browser->CanReloadContents(embedder_web_contents_);
2559 }
2560
IsViewSourceEnabled() const2561 bool RenderViewContextMenu::IsViewSourceEnabled() const {
2562 if (!!extensions::MimeHandlerViewGuest::FromWebContents(
2563 source_web_contents_)) {
2564 return false;
2565 }
2566 return (params_.media_type != ContextMenuDataMediaType::kPlugin) &&
2567 embedder_web_contents_->GetController().CanViewSource();
2568 }
2569
IsDevCommandEnabled(int id) const2570 bool RenderViewContextMenu::IsDevCommandEnabled(int id) const {
2571 if (id == IDC_CONTENT_CONTEXT_INSPECTELEMENT ||
2572 id == IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE) {
2573 PrefService* prefs = GetPrefs(browser_context_);
2574 if (!prefs->GetBoolean(prefs::kWebKitJavascriptEnabled))
2575 return false;
2576
2577 // Don't enable the web inspector if the developer tools are disabled via
2578 // the preference dev-tools-disabled.
2579 if (!DevToolsWindow::AllowDevToolsFor(GetProfile(), source_web_contents_))
2580 return false;
2581 }
2582
2583 return true;
2584 }
2585
IsTranslateEnabled() const2586 bool RenderViewContextMenu::IsTranslateEnabled() const {
2587 ChromeTranslateClient* chrome_translate_client =
2588 ChromeTranslateClient::FromWebContents(embedder_web_contents_);
2589 // If no |chrome_translate_client| attached with this WebContents or we're
2590 // viewing in a MimeHandlerViewGuest translate will be disabled.
2591 if (!chrome_translate_client ||
2592 !!extensions::MimeHandlerViewGuest::FromWebContents(
2593 source_web_contents_)) {
2594 return false;
2595 }
2596 std::string original_lang =
2597 chrome_translate_client->GetLanguageState().original_language();
2598 std::string target_lang = GetTargetLanguage();
2599 // Note that we intentionally enable the menu even if the original and
2600 // target languages are identical. This is to give a way to user to
2601 // translate a page that might contains text fragments in a different
2602 // language.
2603 return ((params_.edit_flags & ContextMenuDataEditFlags::kCanTranslate) !=
2604 0) &&
2605 !original_lang.empty() && // Did we receive the page language yet?
2606 !chrome_translate_client->GetLanguageState().IsPageTranslated() &&
2607 // There are some application locales which can't be used as a
2608 // target language for translation. In that case GetTargetLanguage()
2609 // may return empty.
2610 !target_lang.empty() &&
2611 // Disable on the Instant Extended NTP.
2612 !search::IsInstantNTP(embedder_web_contents_);
2613 }
2614
IsSaveLinkAsEnabled() const2615 bool RenderViewContextMenu::IsSaveLinkAsEnabled() const {
2616 PrefService* local_state = g_browser_process->local_state();
2617 DCHECK(local_state);
2618 // Test if file-selection dialogs are forbidden by policy.
2619 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
2620 return false;
2621
2622 PolicyBlocklistService* service =
2623 PolicyBlocklistFactory::GetForBrowserContext(browser_context_);
2624 if (service->GetURLBlocklistState(params_.link_url) ==
2625 policy::URLBlocklist::URLBlocklistState::URL_IN_BLOCKLIST) {
2626 return false;
2627 }
2628
2629 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
2630 Profile* const profile = Profile::FromBrowserContext(browser_context_);
2631 if (profile->IsChild()) {
2632 SupervisedUserService* supervised_user_service =
2633 SupervisedUserServiceFactory::GetForProfile(profile);
2634 SupervisedUserURLFilter* url_filter =
2635 supervised_user_service->GetURLFilter();
2636 if (url_filter->GetFilteringBehaviorForURL(params_.link_url) !=
2637 SupervisedUserURLFilter::FilteringBehavior::ALLOW)
2638 return false;
2639 }
2640 #endif
2641
2642 return params_.link_url.is_valid() &&
2643 ProfileIOData::IsHandledProtocol(params_.link_url.scheme());
2644 }
2645
IsSaveImageAsEnabled() const2646 bool RenderViewContextMenu::IsSaveImageAsEnabled() const {
2647 PrefService* local_state = g_browser_process->local_state();
2648 DCHECK(local_state);
2649 // Test if file-selection dialogs are forbidden by policy.
2650 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
2651 return false;
2652
2653 return params_.has_image_contents;
2654 }
2655
IsSaveAsEnabled() const2656 bool RenderViewContextMenu::IsSaveAsEnabled() const {
2657 PrefService* local_state = g_browser_process->local_state();
2658 DCHECK(local_state);
2659 // Test if file-selection dialogs are forbidden by policy.
2660 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
2661 return false;
2662
2663 const GURL& url = params_.src_url;
2664 bool can_save = (params_.media_flags & WebContextMenuData::kMediaCanSave) &&
2665 url.is_valid() &&
2666 ProfileIOData::IsHandledProtocol(url.scheme());
2667 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
2668 // Do not save the preview PDF on the print preview page.
2669 can_save = can_save &&
2670 !(printing::PrintPreviewDialogController::IsPrintPreviewURL(url));
2671 #endif
2672 return can_save;
2673 }
2674
IsSavePageEnabled() const2675 bool RenderViewContextMenu::IsSavePageEnabled() const {
2676 CoreTabHelper* core_tab_helper =
2677 CoreTabHelper::FromWebContents(embedder_web_contents_);
2678 if (!core_tab_helper)
2679 return false;
2680
2681 Browser* browser = GetBrowser();
2682 if (browser && !browser->CanSaveContents(embedder_web_contents_))
2683 return false;
2684
2685 PrefService* local_state = g_browser_process->local_state();
2686 DCHECK(local_state);
2687 // Test if file-selection dialogs are forbidden by policy.
2688 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
2689 return false;
2690
2691 // We save the last committed entry (which the user is looking at), as
2692 // opposed to any pending URL that hasn't committed yet.
2693 NavigationEntry* entry =
2694 embedder_web_contents_->GetController().GetLastCommittedEntry();
2695 return content::IsSavableURL(entry ? entry->GetURL() : GURL());
2696 }
2697
IsPasteEnabled() const2698 bool RenderViewContextMenu::IsPasteEnabled() const {
2699 if (!(params_.edit_flags & ContextMenuDataEditFlags::kCanPaste))
2700 return false;
2701
2702 std::vector<base::string16> types;
2703 ui::Clipboard::GetForCurrentThread()->ReadAvailableTypes(
2704 ui::ClipboardBuffer::kCopyPaste,
2705 CreateDataEndpoint(/*notify_if_restricted=*/false).get(), &types);
2706 return !types.empty();
2707 }
2708
IsPasteAndMatchStyleEnabled() const2709 bool RenderViewContextMenu::IsPasteAndMatchStyleEnabled() const {
2710 if (!(params_.edit_flags & ContextMenuDataEditFlags::kCanPaste))
2711 return false;
2712
2713 return ui::Clipboard::GetForCurrentThread()->IsFormatAvailable(
2714 ui::ClipboardFormatType::GetPlainTextType(),
2715 ui::ClipboardBuffer::kCopyPaste,
2716 CreateDataEndpoint(/*notify_if_restricted=*/false).get());
2717 }
2718
IsPrintPreviewEnabled() const2719 bool RenderViewContextMenu::IsPrintPreviewEnabled() const {
2720 if (params_.media_type != ContextMenuDataMediaType::kNone &&
2721 !(params_.media_flags & WebContextMenuData::kMediaCanPrint)) {
2722 return false;
2723 }
2724
2725 Browser* browser = GetBrowser();
2726 return browser && chrome::CanPrint(browser);
2727 }
2728
IsQRCodeGeneratorEnabled() const2729 bool RenderViewContextMenu::IsQRCodeGeneratorEnabled() const {
2730 if (!GetBrowser())
2731 return false;
2732
2733 NavigationEntry* entry =
2734 embedder_web_contents_->GetController().GetLastCommittedEntry();
2735 if (!entry)
2736 return false;
2737
2738 bool incognito = browser_context_->IsOffTheRecord();
2739 return qrcode_generator::QRCodeGeneratorBubbleController::
2740 IsGeneratorAvailable(entry->GetURL(), incognito);
2741 }
2742
AppendQRCodeGeneratorItem(bool for_image,bool draw_icon)2743 void RenderViewContextMenu::AppendQRCodeGeneratorItem(bool for_image,
2744 bool draw_icon) {
2745 if (!IsQRCodeGeneratorEnabled())
2746 return;
2747 auto string_id = for_image ? IDS_CONTEXT_MENU_GENERATE_QR_CODE_IMAGE
2748 : IDS_CONTEXT_MENU_GENERATE_QR_CODE_PAGE;
2749 #if defined(OS_MAC)
2750 draw_icon = false;
2751 #endif
2752 if (draw_icon) {
2753 menu_model_.AddItemWithStringIdAndIcon(
2754 IDC_CONTENT_CONTEXT_GENERATE_QR_CODE, string_id,
2755 ui::ImageModel::FromVectorIcon(kQrcodeGeneratorIcon));
2756 } else {
2757 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_GENERATE_QR_CODE,
2758 string_id);
2759 }
2760 }
2761
2762 std::unique_ptr<ui::DataTransferEndpoint>
CreateDataEndpoint(bool notify_if_restricted) const2763 RenderViewContextMenu::CreateDataEndpoint(bool notify_if_restricted) const {
2764 RenderFrameHost* render_frame_host = GetRenderFrameHost();
2765 if (render_frame_host) {
2766 return std::make_unique<ui::DataTransferEndpoint>(
2767 render_frame_host->GetLastCommittedOrigin(), notify_if_restricted);
2768 }
2769 return nullptr;
2770 }
2771
IsRouteMediaEnabled() const2772 bool RenderViewContextMenu::IsRouteMediaEnabled() const {
2773 if (!media_router::MediaRouterEnabled(browser_context_))
2774 return false;
2775
2776 Browser* browser = GetBrowser();
2777 if (!browser)
2778 return false;
2779
2780 // Disable the command if there is an active modal dialog.
2781 // We don't use |source_web_contents_| here because it could be the
2782 // WebContents for something that's not the current tab (e.g., WebUI
2783 // modal dialog).
2784 WebContents* web_contents =
2785 browser->tab_strip_model()->GetActiveWebContents();
2786 if (!web_contents)
2787 return false;
2788
2789 const web_modal::WebContentsModalDialogManager* manager =
2790 web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
2791 return !manager || !manager->IsDialogActive();
2792 }
2793
IsOpenLinkOTREnabled() const2794 bool RenderViewContextMenu::IsOpenLinkOTREnabled() const {
2795 if (browser_context_->IsOffTheRecord() || !params_.link_url.is_valid())
2796 return false;
2797
2798 if (!IsURLAllowedInIncognito(params_.link_url, browser_context_))
2799 return false;
2800
2801 IncognitoModePrefs::Availability incognito_avail =
2802 IncognitoModePrefs::GetAvailability(GetPrefs(browser_context_));
2803 return incognito_avail != IncognitoModePrefs::DISABLED;
2804 }
2805
ExecOpenWebApp()2806 void RenderViewContextMenu::ExecOpenWebApp() {
2807 base::Optional<web_app::AppId> app_id =
2808 web_app::FindInstalledAppWithUrlInScope(
2809 Profile::FromBrowserContext(browser_context_), params_.link_url);
2810 // |app_id| could be nullopt if it has been uninstalled since the user
2811 // opened the context menu.
2812 if (!app_id)
2813 return;
2814
2815 apps::AppLaunchParams launch_params(
2816 *app_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
2817 WindowOpenDisposition::CURRENT_TAB,
2818 apps::mojom::AppLaunchSource::kSourceContextMenu);
2819 launch_params.override_url = params_.link_url;
2820 apps::AppServiceProxyFactory::GetForProfile(GetProfile())
2821 ->BrowserAppLauncher()
2822 ->LaunchAppWithParams(std::move(launch_params));
2823 }
2824
ExecProtocolHandler(int event_flags,int handler_index)2825 void RenderViewContextMenu::ExecProtocolHandler(int event_flags,
2826 int handler_index) {
2827 ProtocolHandlerRegistry::ProtocolHandlerList handlers =
2828 GetHandlersForLinkUrl();
2829 if (handlers.empty())
2830 return;
2831
2832 base::RecordAction(
2833 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Open"));
2834 WindowOpenDisposition disposition = ui::DispositionFromEventFlags(
2835 event_flags, WindowOpenDisposition::NEW_FOREGROUND_TAB);
2836 OpenURL(handlers[handler_index].TranslateUrl(params_.link_url),
2837 GetDocumentURL(params_), disposition, ui::PAGE_TRANSITION_LINK);
2838 }
2839
ExecOpenLinkInProfile(int profile_index)2840 void RenderViewContextMenu::ExecOpenLinkInProfile(int profile_index) {
2841 DCHECK_GE(profile_index, 0);
2842 DCHECK_LE(profile_index, static_cast<int>(profile_link_paths_.size()));
2843
2844 base::FilePath profile_path = profile_link_paths_[profile_index];
2845 profiles::SwitchToProfile(
2846 profile_path, false,
2847 base::Bind(OnProfileCreated, params_.link_url,
2848 CreateReferrer(params_.link_url, params_)));
2849 }
2850
ExecInspectElement()2851 void RenderViewContextMenu::ExecInspectElement() {
2852 base::RecordAction(UserMetricsAction("DevTools_InspectElement"));
2853 RenderFrameHost* render_frame_host = GetRenderFrameHost();
2854 if (!render_frame_host)
2855 return;
2856 DevToolsWindow::InspectElement(render_frame_host, params_.x, params_.y);
2857 }
2858
ExecInspectBackgroundPage()2859 void RenderViewContextMenu::ExecInspectBackgroundPage() {
2860 const Extension* platform_app = GetExtension();
2861 DCHECK(platform_app);
2862 DCHECK(platform_app->is_platform_app());
2863
2864 extensions::devtools_util::InspectBackgroundPage(platform_app, GetProfile());
2865 }
2866
ExecSaveLinkAs()2867 void RenderViewContextMenu::ExecSaveLinkAs() {
2868 RenderFrameHost* render_frame_host = GetRenderFrameHost();
2869 if (!render_frame_host)
2870 return;
2871
2872 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
2873
2874 const GURL& url = params_.link_url;
2875
2876 net::NetworkTrafficAnnotationTag traffic_annotation =
2877 net::DefineNetworkTrafficAnnotation("render_view_context_menu", R"(
2878 semantics {
2879 sender: "Save Link As"
2880 description: "Saving url to local file."
2881 trigger:
2882 "The user selects the 'Save link as...' command in the context "
2883 "menu."
2884 data: "None."
2885 destination: WEBSITE
2886 }
2887 policy {
2888 cookies_allowed: YES
2889 cookies_store: "user"
2890 setting:
2891 "This feature cannot be disabled by settings. The request is made "
2892 "only if user chooses 'Save link as...' in the context menu."
2893 policy_exception_justification: "Not implemented."
2894 })");
2895
2896 auto dl_params = std::make_unique<DownloadUrlParameters>(
2897 url, render_frame_host->GetProcess()->GetID(),
2898 render_frame_host->GetRoutingID(), traffic_annotation);
2899 content::Referrer referrer = CreateReferrer(url, params_);
2900 dl_params->set_referrer(referrer.url);
2901 dl_params->set_referrer_policy(
2902 content::Referrer::ReferrerPolicyForUrlRequest(referrer.policy));
2903 dl_params->set_referrer_encoding(params_.frame_charset);
2904 dl_params->set_suggested_name(params_.suggested_filename);
2905 dl_params->set_prompt(true);
2906 dl_params->set_download_source(download::DownloadSource::CONTEXT_MENU);
2907
2908 BrowserContext::GetDownloadManager(browser_context_)
2909 ->DownloadUrl(std::move(dl_params));
2910 }
2911
ExecSaveAs()2912 void RenderViewContextMenu::ExecSaveAs() {
2913 bool is_large_data_url =
2914 params_.has_image_contents && params_.src_url.is_empty();
2915 if (params_.media_type == ContextMenuDataMediaType::kCanvas ||
2916 (params_.media_type == ContextMenuDataMediaType::kImage &&
2917 is_large_data_url)) {
2918 RenderFrameHost* frame_host = GetRenderFrameHost();
2919 if (frame_host)
2920 frame_host->SaveImageAt(params_.x, params_.y);
2921 } else {
2922 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
2923 const GURL& url = params_.src_url;
2924 content::Referrer referrer = CreateReferrer(url, params_);
2925 std::string headers;
2926 source_web_contents_->SaveFrameWithHeaders(url, referrer, headers,
2927 params_.suggested_filename);
2928 }
2929 }
2930
ExecExitFullscreen()2931 void RenderViewContextMenu::ExecExitFullscreen() {
2932 Browser* browser = GetBrowser();
2933 if (!browser) {
2934 NOTREACHED();
2935 return;
2936 }
2937
2938 browser->exclusive_access_manager()->ExitExclusiveAccess();
2939 }
2940
ExecCopyLinkText()2941 void RenderViewContextMenu::ExecCopyLinkText() {
2942 ui::ScopedClipboardWriter scw(
2943 ui::ClipboardBuffer::kCopyPaste,
2944 CreateDataEndpoint(/*notify_if_restricted=*/true));
2945 scw.WriteText(params_.link_text);
2946 }
2947
ExecCopyImageAt()2948 void RenderViewContextMenu::ExecCopyImageAt() {
2949 RenderFrameHost* frame_host = GetRenderFrameHost();
2950 if (frame_host)
2951 frame_host->CopyImageAt(params_.x, params_.y);
2952 }
2953
ExecSearchWebForImage()2954 void RenderViewContextMenu::ExecSearchWebForImage() {
2955 CoreTabHelper* core_tab_helper =
2956 CoreTabHelper::FromWebContents(source_web_contents_);
2957 if (!core_tab_helper)
2958 return;
2959 RenderFrameHost* render_frame_host = GetRenderFrameHost();
2960 if (!render_frame_host)
2961 return;
2962 core_tab_helper->SearchByImageInNewTab(render_frame_host, params().src_url);
2963 }
2964
ExecLoadImage()2965 void RenderViewContextMenu::ExecLoadImage() {
2966 RenderFrameHost* render_frame_host = GetRenderFrameHost();
2967 if (!render_frame_host)
2968 return;
2969 mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame> chrome_render_frame;
2970 render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
2971 &chrome_render_frame);
2972 chrome_render_frame->RequestReloadImageForContextNode();
2973 }
2974
ExecPlayPause()2975 void RenderViewContextMenu::ExecPlayPause() {
2976 bool play = !!(params_.media_flags & WebContextMenuData::kMediaPaused);
2977 if (play)
2978 base::RecordAction(UserMetricsAction("MediaContextMenu_Play"));
2979 else
2980 base::RecordAction(UserMetricsAction("MediaContextMenu_Pause"));
2981
2982 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
2983 blink::mojom::MediaPlayerAction(
2984 blink::mojom::MediaPlayerActionType::kPlay, play));
2985 }
2986
ExecMute()2987 void RenderViewContextMenu::ExecMute() {
2988 bool mute = !(params_.media_flags & WebContextMenuData::kMediaMuted);
2989 if (mute)
2990 base::RecordAction(UserMetricsAction("MediaContextMenu_Mute"));
2991 else
2992 base::RecordAction(UserMetricsAction("MediaContextMenu_Unmute"));
2993
2994 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
2995 blink::mojom::MediaPlayerAction(
2996 blink::mojom::MediaPlayerActionType::kMute, mute));
2997 }
2998
ExecLoop()2999 void RenderViewContextMenu::ExecLoop() {
3000 base::RecordAction(UserMetricsAction("MediaContextMenu_Loop"));
3001 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
3002 blink::mojom::MediaPlayerAction(
3003 blink::mojom::MediaPlayerActionType::kLoop,
3004 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP)));
3005 }
3006
ExecControls()3007 void RenderViewContextMenu::ExecControls() {
3008 base::RecordAction(UserMetricsAction("MediaContextMenu_Controls"));
3009 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
3010 blink::mojom::MediaPlayerAction(
3011 blink::mojom::MediaPlayerActionType::kControls,
3012 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS)));
3013 }
3014
ExecRotateCW()3015 void RenderViewContextMenu::ExecRotateCW() {
3016 base::RecordAction(UserMetricsAction("PluginContextMenu_RotateClockwise"));
3017 PluginActionAt(gfx::Point(params_.x, params_.y),
3018 blink::mojom::PluginActionType::kRotate90Clockwise);
3019 }
3020
ExecRotateCCW()3021 void RenderViewContextMenu::ExecRotateCCW() {
3022 base::RecordAction(
3023 UserMetricsAction("PluginContextMenu_RotateCounterclockwise"));
3024 PluginActionAt(gfx::Point(params_.x, params_.y),
3025 blink::mojom::PluginActionType::kRotate90Counterclockwise);
3026 }
3027
ExecReloadPackagedApp()3028 void RenderViewContextMenu::ExecReloadPackagedApp() {
3029 const Extension* platform_app = GetExtension();
3030 DCHECK(platform_app);
3031 DCHECK(platform_app->is_platform_app());
3032
3033 extensions::ExtensionSystem::Get(browser_context_)
3034 ->extension_service()
3035 ->ReloadExtension(platform_app->id());
3036 }
3037
ExecRestartPackagedApp()3038 void RenderViewContextMenu::ExecRestartPackagedApp() {
3039 const Extension* platform_app = GetExtension();
3040 DCHECK(platform_app);
3041 DCHECK(platform_app->is_platform_app());
3042
3043 apps::AppLoadService::Get(GetProfile())
3044 ->RestartApplication(platform_app->id());
3045 }
3046
ExecPrint()3047 void RenderViewContextMenu::ExecPrint() {
3048 #if BUILDFLAG(ENABLE_PRINTING)
3049 if (params_.media_type != ContextMenuDataMediaType::kNone) {
3050 RenderFrameHost* rfh = GetRenderFrameHost();
3051 if (rfh) {
3052 mojo::AssociatedRemote<printing::mojom::PrintRenderFrame> remote;
3053 rfh->GetRemoteAssociatedInterfaces()->GetInterface(&remote);
3054 remote->PrintNodeUnderContextMenu();
3055 }
3056 return;
3057 }
3058
3059 printing::StartPrint(
3060 source_web_contents_, mojo::NullAssociatedRemote(),
3061 GetPrefs(browser_context_)->GetBoolean(prefs::kPrintPreviewDisabled),
3062 !params_.selection_text.empty());
3063 #endif // BUILDFLAG(ENABLE_PRINTING)
3064 }
3065
ExecRouteMedia()3066 void RenderViewContextMenu::ExecRouteMedia() {
3067 if (!media_router::MediaRouterEnabled(browser_context_))
3068 return;
3069
3070 media_router::MediaRouterDialogController* dialog_controller =
3071 media_router::MediaRouterDialogController::GetOrCreateForWebContents(
3072 embedder_web_contents_);
3073 if (!dialog_controller)
3074 return;
3075
3076 dialog_controller->ShowMediaRouterDialog(
3077 media_router::MediaRouterDialogOpenOrigin::CONTEXTUAL_MENU);
3078 media_router::MediaRouterMetrics::RecordMediaRouterDialogOrigin(
3079 media_router::MediaRouterDialogOpenOrigin::CONTEXTUAL_MENU);
3080 }
3081
ExecTranslate()3082 void RenderViewContextMenu::ExecTranslate() {
3083 ChromeTranslateClient* chrome_translate_client =
3084 ChromeTranslateClient::FromWebContents(embedder_web_contents_);
3085 if (!chrome_translate_client)
3086 return;
3087
3088 translate::TranslateManager* manager =
3089 chrome_translate_client->GetTranslateManager();
3090 DCHECK(manager);
3091 manager->InitiateManualTranslation(true, true);
3092 }
3093
ExecLanguageSettings(int event_flags)3094 void RenderViewContextMenu::ExecLanguageSettings(int event_flags) {
3095 WindowOpenDisposition disposition = ui::DispositionFromEventFlags(
3096 event_flags, WindowOpenDisposition::NEW_FOREGROUND_TAB);
3097 GURL url = chrome::GetSettingsUrl(chrome::kLanguageOptionsSubPage);
3098 OpenURL(url, GURL(), disposition, ui::PAGE_TRANSITION_LINK);
3099 }
3100
ExecProtocolHandlerSettings(int event_flags)3101 void RenderViewContextMenu::ExecProtocolHandlerSettings(int event_flags) {
3102 base::RecordAction(
3103 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Settings"));
3104 WindowOpenDisposition disposition = ui::DispositionFromEventFlags(
3105 event_flags, WindowOpenDisposition::NEW_FOREGROUND_TAB);
3106 GURL url = chrome::GetSettingsUrl(chrome::kHandlerSettingsSubPage);
3107 OpenURL(url, GURL(), disposition, ui::PAGE_TRANSITION_LINK);
3108 }
3109
ExecPictureInPicture()3110 void RenderViewContextMenu::ExecPictureInPicture() {
3111 if (!base::FeatureList::IsEnabled(media::kPictureInPicture))
3112 return;
3113
3114 bool picture_in_picture_active =
3115 IsCommandIdChecked(IDC_CONTENT_CONTEXT_PICTUREINPICTURE);
3116
3117 if (picture_in_picture_active) {
3118 base::RecordAction(
3119 UserMetricsAction("MediaContextMenu_ExitPictureInPicture"));
3120 } else {
3121 base::RecordAction(
3122 UserMetricsAction("MediaContextMenu_EnterPictureInPicture"));
3123 }
3124
3125 MediaPlayerActionAt(
3126 gfx::Point(params_.x, params_.y),
3127 blink::mojom::MediaPlayerAction(
3128 blink::mojom::MediaPlayerActionType::kPictureInPicture,
3129 !picture_in_picture_active));
3130 }
3131
MediaPlayerActionAt(const gfx::Point & location,const blink::mojom::MediaPlayerAction & action)3132 void RenderViewContextMenu::MediaPlayerActionAt(
3133 const gfx::Point& location,
3134 const blink::mojom::MediaPlayerAction& action) {
3135 RenderFrameHost* frame_host = GetRenderFrameHost();
3136 if (frame_host)
3137 frame_host->ExecuteMediaPlayerActionAtLocation(location, action);
3138 }
3139
PluginActionAt(const gfx::Point & location,blink::mojom::PluginActionType plugin_action)3140 void RenderViewContextMenu::PluginActionAt(
3141 const gfx::Point& location,
3142 blink::mojom::PluginActionType plugin_action) {
3143 source_web_contents_->GetMainFrame()
3144 ->GetRenderViewHost()
3145 ->ExecutePluginActionAtLocation(location, plugin_action);
3146 }
3147
GetBrowser() const3148 Browser* RenderViewContextMenu::GetBrowser() const {
3149 return chrome::FindBrowserWithWebContents(embedder_web_contents_);
3150 }
3151