1 // Copyright 2013 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.
5 #include "chrome/browser/tab_contents/render_view_context_menu.h"
11 #include "apps/app_load_service.h"
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_member.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/time/time.h"
22 #include "chrome/app/chrome_command_ids.h"
23 #include "chrome/browser/app_mode/app_mode_utils.h"
24 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
25 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
26 #include "chrome/browser/autocomplete/autocomplete_match.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/browser/chrome_notification_types.h"
29 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
30 #include "chrome/browser/devtools/devtools_window.h"
31 #include "chrome/browser/download/download_service.h"
32 #include "chrome/browser/download/download_service_factory.h"
33 #include "chrome/browser/download/download_stats.h"
34 #include "chrome/browser/extensions/devtools_util.h"
35 #include "chrome/browser/extensions/extension_host.h"
36 #include "chrome/browser/extensions/extension_service.h"
37 #include "chrome/browser/extensions/extension_system.h"
38 #include "chrome/browser/google/google_util.h"
39 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
40 #include "chrome/browser/prefs/incognito_mode_prefs.h"
41 #include "chrome/browser/profiles/profile.h"
42 #include "chrome/browser/profiles/profile_io_data.h"
43 #include "chrome/browser/search/search.h"
44 #include "chrome/browser/search_engines/search_terms_data.h"
45 #include "chrome/browser/search_engines/template_url.h"
46 #include "chrome/browser/search_engines/template_url_service.h"
47 #include "chrome/browser/search_engines/template_url_service_factory.h"
48 #include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
49 #include "chrome/browser/spellchecker/spellcheck_service.h"
50 #include "chrome/browser/tab_contents/retargeting_details.h"
51 #include "chrome/browser/tab_contents/spellchecker_submenu_observer.h"
52 #include "chrome/browser/tab_contents/spelling_menu_observer.h"
53 #include "chrome/browser/translate/translate_manager.h"
54 #include "chrome/browser/translate/translate_prefs.h"
55 #include "chrome/browser/translate/translate_tab_helper.h"
56 #include "chrome/browser/ui/browser.h"
57 #include "chrome/browser/ui/browser_commands.h"
58 #include "chrome/browser/ui/browser_finder.h"
59 #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h"
60 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
61 #include "chrome/common/chrome_constants.h"
62 #include "chrome/common/chrome_switches.h"
63 #include "chrome/common/content_restriction.h"
64 #include "chrome/common/net/url_util.h"
65 #include "chrome/common/pref_names.h"
66 #include "chrome/common/render_messages.h"
67 #include "chrome/common/spellcheck_messages.h"
68 #include "chrome/common/url_constants.h"
69 #include "content/public/browser/child_process_security_policy.h"
70 #include "content/public/browser/download_manager.h"
71 #include "content/public/browser/download_save_info.h"
72 #include "content/public/browser/download_url_parameters.h"
73 #include "content/public/browser/navigation_details.h"
74 #include "content/public/browser/navigation_entry.h"
75 #include "content/public/browser/notification_service.h"
76 #include "content/public/browser/render_process_host.h"
77 #include "content/public/browser/render_view_host.h"
78 #include "content/public/browser/render_widget_host_view.h"
79 #include "content/public/browser/user_metrics.h"
80 #include "content/public/browser/web_contents.h"
81 #include "content/public/common/menu_item.h"
82 #include "content/public/common/ssl_status.h"
83 #include "content/public/common/url_utils.h"
84 #include "extensions/browser/view_type_utils.h"
85 #include "extensions/common/extension.h"
86 #include "grit/generated_resources.h"
87 #include "net/base/escape.h"
88 #include "third_party/WebKit/public/web/WebContextMenuData.h"
89 #include "third_party/WebKit/public/web/WebMediaPlayerAction.h"
90 #include "third_party/WebKit/public/web/WebPluginAction.h"
91 #include "ui/base/clipboard/clipboard.h"
92 #include "ui/base/l10n/l10n_util.h"
93 #include "ui/gfx/favicon_size.h"
94 #include "ui/gfx/point.h"
95 #include "ui/gfx/size.h"
96 #include "ui/gfx/text_elider.h"
98 #if defined(ENABLE_PRINTING)
99 #include "chrome/common/print_messages.h"
101 #if defined(ENABLE_FULL_PRINTING)
102 #include "chrome/browser/printing/print_preview_context_menu_observer.h"
103 #include "chrome/browser/printing/print_preview_dialog_controller.h"
104 #include "chrome/browser/printing/print_view_manager.h"
106 #include "chrome/browser/printing/print_view_manager_basic.h"
107 #endif // defined(ENABLE_FULL_PRINTING)
108 #endif // defined(ENABLE_PRINTING)
110 using base::UserMetricsAction
;
111 using blink::WebContextMenuData
;
112 using blink::WebMediaPlayerAction
;
113 using blink::WebPluginAction
;
114 using blink::WebString
;
116 using content::BrowserContext
;
117 using content::ChildProcessSecurityPolicy
;
118 using content::DownloadManager
;
119 using content::DownloadUrlParameters
;
120 using content::NavigationController
;
121 using content::NavigationEntry
;
122 using content::OpenURLParams
;
123 using content::RenderViewHost
;
124 using content::SSLStatus
;
125 using content::WebContents
;
126 using extensions::Extension
;
127 using extensions::MenuItem
;
128 using extensions::MenuManager
;
132 const int kImageSearchThumbnailMinSize
= 300 * 300;
133 const int kImageSearchThumbnailMaxWidth
= 600;
134 const int kImageSearchThumbnailMaxHeight
= 600;
136 // Maps UMA enumeration to IDC. IDC could be changed so we can't use
137 // just them and |UMA_HISTOGRAM_CUSTOM_ENUMERATION|.
138 // Never change mapping or reuse |enum_id|. Always push back new items.
139 // Items that is not used any more by |RenderViewContextMenu.ExecuteCommand|
140 // could be deleted, but don't change the rest of |kUmaEnumToControlId|.
141 const struct UmaEnumCommandIdPair
{
144 } kUmaEnumToControlId
[] = {
145 { 0, IDC_CONTENT_CONTEXT_CUSTOM_FIRST
},
146 { 1, IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
},
147 { 2, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
},
148 { 3, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
},
149 { 4, IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
},
150 { 5, IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
},
151 { 6, IDC_CONTENT_CONTEXT_SAVELINKAS
},
152 { 7, IDC_CONTENT_CONTEXT_SAVEAVAS
},
153 { 8, IDC_CONTENT_CONTEXT_SAVEIMAGEAS
},
154 { 9, IDC_CONTENT_CONTEXT_COPYLINKLOCATION
},
155 { 10, IDC_CONTENT_CONTEXT_COPYIMAGELOCATION
},
156 { 11, IDC_CONTENT_CONTEXT_COPYAVLOCATION
},
157 { 12, IDC_CONTENT_CONTEXT_COPYIMAGE
},
158 { 13, IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
},
159 { 14, IDC_CONTENT_CONTEXT_OPENAVNEWTAB
},
160 { 15, IDC_CONTENT_CONTEXT_PLAYPAUSE
},
161 { 16, IDC_CONTENT_CONTEXT_MUTE
},
162 { 17, IDC_CONTENT_CONTEXT_LOOP
},
163 { 18, IDC_CONTENT_CONTEXT_CONTROLS
},
164 { 19, IDC_CONTENT_CONTEXT_ROTATECW
},
165 { 20, IDC_CONTENT_CONTEXT_ROTATECCW
},
168 { 23, IDC_SAVE_PAGE
},
170 { 25, IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
},
171 { 26, IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP
},
173 { 28, IDC_VIEW_SOURCE
},
174 { 29, IDC_CONTENT_CONTEXT_INSPECTELEMENT
},
175 { 30, IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
},
176 { 31, IDC_CONTENT_CONTEXT_VIEWPAGEINFO
},
177 { 32, IDC_CONTENT_CONTEXT_TRANSLATE
},
178 { 33, IDC_CONTENT_CONTEXT_RELOADFRAME
},
179 { 34, IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE
},
180 { 35, IDC_CONTENT_CONTEXT_VIEWFRAMEINFO
},
181 { 36, IDC_CONTENT_CONTEXT_UNDO
},
182 { 37, IDC_CONTENT_CONTEXT_REDO
},
183 { 38, IDC_CONTENT_CONTEXT_CUT
},
184 { 39, IDC_CONTENT_CONTEXT_COPY
},
185 { 40, IDC_CONTENT_CONTEXT_PASTE
},
186 { 41, IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
},
187 { 42, IDC_CONTENT_CONTEXT_DELETE
},
188 { 43, IDC_CONTENT_CONTEXT_SELECTALL
},
189 { 44, IDC_CONTENT_CONTEXT_SEARCHWEBFOR
},
190 { 45, IDC_CONTENT_CONTEXT_GOTOURL
},
191 { 46, IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS
},
192 { 47, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS
},
193 { 48, IDC_CONTENT_CONTEXT_ADDSEARCHENGINE
},
194 { 49, IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES
},
195 { 50, IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT
},
196 { 51, IDC_SPEECH_INPUT_MENU
},
197 { 52, IDC_CONTENT_CONTEXT_OPENLINKWITH
},
198 { 53, IDC_CHECK_SPELLING_WHILE_TYPING
},
199 { 54, IDC_SPELLCHECK_MENU
},
200 { 55, IDC_CONTENT_CONTEXT_SPELLING_TOGGLE
},
201 { 56, IDC_SPELLCHECK_LANGUAGES_FIRST
},
202 { 57, IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
},
203 // Add new items here and use |enum_id| from the next line.
204 { 58, 0 }, // Must be the last. Increment |enum_id| when new IDC was added.
207 // Collapses large ranges of ids before looking for UMA enum.
208 int CollapleCommandsForUMA(int id
) {
209 if (id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
210 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
211 return IDC_CONTENT_CONTEXT_CUSTOM_FIRST
;
214 if (id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
215 id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
216 return IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
;
219 if (id
>= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
&&
220 id
<= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST
) {
221 return IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
;
224 if (id
>= IDC_SPELLCHECK_LANGUAGES_FIRST
&&
225 id
<= IDC_SPELLCHECK_LANGUAGES_LAST
) {
226 return IDC_SPELLCHECK_LANGUAGES_FIRST
;
232 // Returns UMA enum value for command specified by |id| or -1 if not found.
233 int FindUMAEnumValueForCommand(int id
) {
234 id
= CollapleCommandsForUMA(id
);
235 const size_t kMappingSize
= arraysize(kUmaEnumToControlId
);
236 for (size_t i
= 0; i
< kMappingSize
; ++i
) {
237 if (kUmaEnumToControlId
[i
].control_id
== id
) {
238 return kUmaEnumToControlId
[i
].enum_id
;
244 // Increments histogram value for used items specified by |id|.
245 void RecordUsedItem(int id
) {
246 int enum_id
= FindUMAEnumValueForCommand(id
);
248 const size_t kMappingSize
= arraysize(kUmaEnumToControlId
);
249 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Used", enum_id
,
250 kUmaEnumToControlId
[kMappingSize
- 1].enum_id
);
252 NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id
;
256 // Increments histogram value for visible context menu item specified by |id|.
257 void RecordShownItem(int id
) {
258 int enum_id
= FindUMAEnumValueForCommand(id
);
260 const size_t kMappingSize
= arraysize(kUmaEnumToControlId
);
261 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Shown", enum_id
,
262 kUmaEnumToControlId
[kMappingSize
- 1].enum_id
);
264 // Just warning here. It's harder to maintain list of all possibly
265 // visible items than executable items.
266 DLOG(ERROR
) << "Update kUmaEnumToControlId. Unhanded IDC: " << id
;
270 // Usually a new tab is expected where this function is used,
271 // however users should be able to open a tab in background
272 // or in a new window.
273 WindowOpenDisposition
ForceNewTabDispositionFromEventFlags(
275 WindowOpenDisposition disposition
=
276 ui::DispositionFromEventFlags(event_flags
);
277 return disposition
== CURRENT_TAB
? NEW_FOREGROUND_TAB
: disposition
;
280 bool IsCustomItemEnabled(const std::vector
<content::MenuItem
>& items
, int id
) {
281 DCHECK(id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
282 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
);
283 for (size_t i
= 0; i
< items
.size(); ++i
) {
284 int action_id
= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
+ items
[i
].action
;
286 return items
[i
].enabled
;
287 if (items
[i
].type
== content::MenuItem::SUBMENU
) {
288 if (IsCustomItemEnabled(items
[i
].submenu
, id
))
295 bool IsCustomItemChecked(const std::vector
<content::MenuItem
>& items
, int id
) {
296 DCHECK(id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
297 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
);
298 for (size_t i
= 0; i
< items
.size(); ++i
) {
299 int action_id
= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
+ items
[i
].action
;
301 return items
[i
].checked
;
302 if (items
[i
].type
== content::MenuItem::SUBMENU
) {
303 if (IsCustomItemChecked(items
[i
].submenu
, id
))
310 const size_t kMaxCustomMenuDepth
= 5;
311 const size_t kMaxCustomMenuTotalItems
= 1000;
313 void AddCustomItemsToMenu(const std::vector
<content::MenuItem
>& items
,
316 ui::SimpleMenuModel::Delegate
* delegate
,
317 ui::SimpleMenuModel
* menu_model
) {
318 if (depth
> kMaxCustomMenuDepth
) {
319 LOG(ERROR
) << "Custom menu too deeply nested.";
322 for (size_t i
= 0; i
< items
.size(); ++i
) {
323 if (IDC_CONTENT_CONTEXT_CUSTOM_FIRST
+ items
[i
].action
>=
324 IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
325 LOG(ERROR
) << "Custom menu action value too big.";
328 if (*total_items
>= kMaxCustomMenuTotalItems
) {
329 LOG(ERROR
) << "Custom menu too large (too many items).";
333 switch (items
[i
].type
) {
334 case content::MenuItem::OPTION
:
336 items
[i
].action
+ IDC_CONTENT_CONTEXT_CUSTOM_FIRST
,
339 case content::MenuItem::CHECKABLE_OPTION
:
340 menu_model
->AddCheckItem(
341 items
[i
].action
+ IDC_CONTENT_CONTEXT_CUSTOM_FIRST
,
344 case content::MenuItem::GROUP
:
345 // TODO(viettrungluu): I don't know what this is supposed to do.
348 case content::MenuItem::SEPARATOR
:
349 menu_model
->AddSeparator(ui::NORMAL_SEPARATOR
);
351 case content::MenuItem::SUBMENU
: {
352 ui::SimpleMenuModel
* submenu
= new ui::SimpleMenuModel(delegate
);
353 AddCustomItemsToMenu(items
[i
].submenu
, depth
+ 1, total_items
, delegate
,
355 menu_model
->AddSubMenu(
356 items
[i
].action
+ IDC_CONTENT_CONTEXT_CUSTOM_FIRST
,
368 void DevToolsInspectElementAt(RenderViewHost
* rvh
, int x
, int y
) {
369 DevToolsWindow::InspectElement(rvh
, x
, y
);
372 // Helper function to escape "&" as "&&".
373 void EscapeAmpersands(base::string16
* text
) {
374 const base::char16 ampersand
[] = {'&', 0};
375 base::ReplaceChars(*text
, ampersand
, base::ASCIIToUTF16("&&"), text
);
381 const size_t RenderViewContextMenu::kMaxSelectionTextLength
= 50;
384 bool RenderViewContextMenu::IsDevToolsURL(const GURL
& url
) {
385 return url
.SchemeIs(chrome::kChromeDevToolsScheme
);
389 bool RenderViewContextMenu::IsInternalResourcesURL(const GURL
& url
) {
390 if (!url
.SchemeIs(chrome::kChromeUIScheme
))
392 return url
.host() == chrome::kChromeUISyncResourcesHost
;
395 static const int kSpellcheckRadioGroup
= 1;
397 RenderViewContextMenu::RenderViewContextMenu(
398 WebContents
* web_contents
,
399 const content::ContextMenuParams
& params
)
401 source_web_contents_(web_contents
),
402 profile_(Profile::FromBrowserContext(web_contents
->GetBrowserContext())),
404 extension_items_(profile_
, this, &menu_model_
,
405 base::Bind(MenuItemMatchesParams
, params_
)),
406 speech_input_submenu_model_(this),
407 protocol_handler_submenu_model_(this),
408 protocol_handler_registry_(
409 ProtocolHandlerRegistryFactory::GetForProfile(profile_
)),
410 command_executed_(false),
412 RenderViewHost
* rvh
= source_web_contents_
->GetRenderViewHost();
413 if (rvh
&& rvh
->GetProcess()->IsGuest())
417 RenderViewContextMenu::~RenderViewContextMenu() {
420 // Menu construction functions -------------------------------------------------
422 void RenderViewContextMenu::Init() {
427 void RenderViewContextMenu::Cancel() {
431 static bool ExtensionPatternMatch(const extensions::URLPatternSet
& patterns
,
433 // No patterns means no restriction, so that implicitly matches.
434 if (patterns
.is_empty())
436 return patterns
.MatchesURL(url
);
440 bool RenderViewContextMenu::ExtensionContextAndPatternMatch(
441 const content::ContextMenuParams
& params
,
442 MenuItem::ContextList contexts
,
443 const extensions::URLPatternSet
& target_url_patterns
) {
444 const bool has_link
= !params
.link_url
.is_empty();
445 const bool has_selection
= !params
.selection_text
.empty();
446 const bool in_frame
= !params
.frame_url
.is_empty();
448 if (contexts
.Contains(MenuItem::ALL
) ||
449 (has_selection
&& contexts
.Contains(MenuItem::SELECTION
)) ||
450 (params
.is_editable
&& contexts
.Contains(MenuItem::EDITABLE
)) ||
451 (in_frame
&& contexts
.Contains(MenuItem::FRAME
)))
454 if (has_link
&& contexts
.Contains(MenuItem::LINK
) &&
455 ExtensionPatternMatch(target_url_patterns
, params
.link_url
))
458 switch (params
.media_type
) {
459 case WebContextMenuData::MediaTypeImage
:
460 if (contexts
.Contains(MenuItem::IMAGE
) &&
461 ExtensionPatternMatch(target_url_patterns
, params
.src_url
))
465 case WebContextMenuData::MediaTypeVideo
:
466 if (contexts
.Contains(MenuItem::VIDEO
) &&
467 ExtensionPatternMatch(target_url_patterns
, params
.src_url
))
471 case WebContextMenuData::MediaTypeAudio
:
472 if (contexts
.Contains(MenuItem::AUDIO
) &&
473 ExtensionPatternMatch(target_url_patterns
, params
.src_url
))
481 // PAGE is the least specific context, so we only examine that if none of the
482 // other contexts apply (except for FRAME, which is included in PAGE for
483 // backwards compatibility).
484 if (!has_link
&& !has_selection
&& !params
.is_editable
&&
485 params
.media_type
== WebContextMenuData::MediaTypeNone
&&
486 contexts
.Contains(MenuItem::PAGE
))
492 static const GURL
& GetDocumentURL(const content::ContextMenuParams
& params
) {
493 return params
.frame_url
.is_empty() ? params
.page_url
: params
.frame_url
;
497 bool RenderViewContextMenu::MenuItemMatchesParams(
498 const content::ContextMenuParams
& params
,
499 const extensions::MenuItem
* item
) {
500 bool match
= ExtensionContextAndPatternMatch(params
, item
->contexts(),
501 item
->target_url_patterns());
505 const GURL
& document_url
= GetDocumentURL(params
);
506 return ExtensionPatternMatch(item
->document_url_patterns(), document_url
);
509 void RenderViewContextMenu::AppendAllExtensionItems() {
510 extension_items_
.Clear();
511 ExtensionService
* service
=
512 extensions::ExtensionSystem::Get(profile_
)->extension_service();
514 return; // In unit-tests, we may not have an ExtensionService.
516 MenuManager
* menu_manager
= MenuManager::Get(profile_
);
520 base::string16 printable_selection_text
= PrintableSelectionText();
521 EscapeAmpersands(&printable_selection_text
);
523 // Get a list of extension id's that have context menu items, and sort by the
524 // top level context menu title of the extension.
525 std::set
<std::string
> ids
= menu_manager
->ExtensionIds();
526 std::vector
<base::string16
> sorted_menu_titles
;
527 std::map
<base::string16
, std::string
> map_ids
;
528 for (std::set
<std::string
>::iterator i
= ids
.begin(); i
!= ids
.end(); ++i
) {
529 const Extension
* extension
= service
->GetExtensionById(*i
, false);
530 // Platform apps have their context menus created directly in
531 // AppendPlatformAppItems.
532 if (extension
&& !extension
->is_platform_app()) {
533 base::string16 menu_title
= extension_items_
.GetTopLevelContextMenuTitle(
534 *i
, printable_selection_text
);
535 map_ids
[menu_title
] = *i
;
536 sorted_menu_titles
.push_back(menu_title
);
539 if (sorted_menu_titles
.empty())
542 const std::string app_locale
= g_browser_process
->GetApplicationLocale();
543 l10n_util::SortStrings16(app_locale
, &sorted_menu_titles
);
546 base::TimeTicks begin
= base::TimeTicks::Now();
547 for (size_t i
= 0; i
< sorted_menu_titles
.size(); ++i
) {
548 const std::string
& id
= map_ids
[sorted_menu_titles
[i
]];
549 extension_items_
.AppendExtensionItems(id
, printable_selection_text
,
553 UMA_HISTOGRAM_TIMES("Extensions.ContextMenus_BuildTime",
554 base::TimeTicks::Now() - begin
);
555 UMA_HISTOGRAM_COUNTS("Extensions.ContextMenus_ItemCount", index
);
558 void RenderViewContextMenu::InitMenu() {
559 if (chrome::IsRunningInForcedAppMode()) {
560 AppendAppModeItems();
564 extensions::ViewType view_type
=
565 extensions::GetViewType(source_web_contents_
);
566 if (view_type
== extensions::VIEW_TYPE_APP_SHELL
) {
567 AppendPlatformAppItems();
569 } else if (view_type
== extensions::VIEW_TYPE_EXTENSION_POPUP
) {
570 AppendPopupExtensionItems();
572 } else if (view_type
== extensions::VIEW_TYPE_PANEL
) {
577 const bool has_link
= !params_
.unfiltered_link_url
.is_empty();
578 const bool has_selection
= !params_
.selection_text
.empty();
580 if (AppendCustomItems()) {
581 // If there's a selection, don't early return when there are custom items,
582 // but fall through to adding the normal ones after the custom ones.
584 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
586 // Don't add items for Pepper menu.
587 if (!params_
.custom_context
.is_pepper_menu
)
588 AppendDeveloperItems();
593 // When no special node or text is selected and selection has no link,
595 if (params_
.media_type
== WebContextMenuData::MediaTypeNone
&&
597 !params_
.is_editable
&&
600 if (!params_
.page_url
.is_empty()) {
601 bool is_devtools
= IsDevToolsURL(params_
.page_url
);
602 if (!is_devtools
&& !IsInternalResourcesURL(params_
.page_url
)) {
604 // Merge in frame items if we clicked within a frame that needs them.
605 if (!params_
.frame_url
.is_empty()) {
606 is_devtools
= IsDevToolsURL(params_
.frame_url
);
607 if (!is_devtools
&& !IsInternalResourcesURL(params_
.frame_url
)) {
608 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
614 DCHECK(params_
.frame_url
.is_empty());
618 // Do not show link related items for guest.
619 if (has_link
&& !is_guest_
) {
621 if (params_
.media_type
!= WebContextMenuData::MediaTypeNone
)
622 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
625 switch (params_
.media_type
) {
626 case WebContextMenuData::MediaTypeNone
:
628 case WebContextMenuData::MediaTypeImage
:
631 case WebContextMenuData::MediaTypeVideo
:
634 case WebContextMenuData::MediaTypeAudio
:
637 case WebContextMenuData::MediaTypePlugin
:
640 #ifdef WEBCONTEXT_MEDIATYPEFILE_DEFINED
641 case WebContextMenuData::MediaTypeFile
:
646 if (params_
.is_editable
)
647 AppendEditableItems();
648 else if (has_selection
)
651 if (!is_guest_
&& has_selection
) {
652 AppendSearchProvider();
653 if (!IsDevToolsURL(params_
.page_url
))
657 if (!IsDevToolsURL(params_
.page_url
) && !is_guest_
)
658 AppendAllExtensionItems();
660 AppendDeveloperItems();
663 #if defined(ENABLE_FULL_PRINTING)
664 if (!print_preview_menu_observer_
.get()) {
665 print_preview_menu_observer_
.reset(
666 new PrintPreviewContextMenuObserver(source_web_contents_
));
669 observers_
.AddObserver(print_preview_menu_observer_
.get());
674 const Extension
* RenderViewContextMenu::GetExtension() const {
675 extensions::ExtensionSystem
* system
=
676 extensions::ExtensionSystem::Get(profile_
);
677 // There is no process manager in some tests.
678 if (!system
->process_manager())
681 return system
->process_manager()->GetExtensionForRenderViewHost(
682 source_web_contents_
->GetRenderViewHost());
685 void RenderViewContextMenu::AppendAppModeItems() {
686 const bool has_selection
= !params_
.selection_text
.empty();
688 if (params_
.is_editable
)
689 AppendEditableItems();
690 else if (has_selection
)
694 void RenderViewContextMenu::AppendPlatformAppItems() {
695 const Extension
* platform_app
= GetExtension();
697 // The RVH might be for a process sandboxed from the extension.
701 DCHECK(platform_app
->is_platform_app());
703 const bool has_selection
= !params_
.selection_text
.empty();
705 // Add undo/redo, cut/copy/paste etc for text fields.
706 if (params_
.is_editable
)
707 AppendEditableItems();
708 else if (has_selection
)
712 extension_items_
.AppendExtensionItems(platform_app
->id(),
713 PrintableSelectionText(), &index
);
715 // Add dev tools for unpacked extensions.
716 if (extensions::Manifest::IsUnpackedLocation(platform_app
->location()) ||
717 CommandLine::ForCurrentProcess()->HasSwitch(
718 switches::kDebugPackedApps
)) {
719 // Add a separator if there are any items already in the menu.
720 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
722 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
,
723 IDS_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
);
724 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP
,
725 IDS_CONTENT_CONTEXT_RESTART_APP
);
726 AppendDeveloperItems();
727 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
,
728 IDS_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
);
732 void RenderViewContextMenu::AppendPopupExtensionItems() {
733 const bool has_selection
= !params_
.selection_text
.empty();
735 if (params_
.is_editable
)
736 AppendEditableItems();
737 else if (has_selection
)
741 AppendSearchProvider();
743 AppendAllExtensionItems();
744 AppendDeveloperItems();
747 void RenderViewContextMenu::AppendPanelItems() {
748 bool has_selection
= !params_
.selection_text
.empty();
750 // Checking link should take precedence before checking selection since on Mac
751 // right-clicking a link will also make it selected.
752 if (params_
.unfiltered_link_url
.is_valid())
755 if (params_
.is_editable
)
756 AppendEditableItems();
757 else if (has_selection
)
760 // Avoid appending extension related items when |extension| is null. This
761 // happens when the panel is navigated to a url outside of the extension's
763 const Extension
* extension
= GetExtension();
765 // Only add extension items from this extension.
767 extension_items_
.AppendExtensionItems(extension
->id(),
768 PrintableSelectionText(), &index
);
772 void RenderViewContextMenu::AddMenuItem(int command_id
,
773 const base::string16
& title
) {
774 menu_model_
.AddItem(command_id
, title
);
777 void RenderViewContextMenu::AddCheckItem(int command_id
,
778 const base::string16
& title
) {
779 menu_model_
.AddCheckItem(command_id
, title
);
782 void RenderViewContextMenu::AddSeparator() {
783 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
786 void RenderViewContextMenu::AddSubMenu(int command_id
,
787 const base::string16
& label
,
788 ui::MenuModel
* model
) {
789 menu_model_
.AddSubMenu(command_id
, label
, model
);
792 void RenderViewContextMenu::UpdateMenuItem(int command_id
,
795 const base::string16
& label
) {
796 // This function needs platform-specific implementation.
800 RenderViewHost
* RenderViewContextMenu::GetRenderViewHost() const {
801 return source_web_contents_
->GetRenderViewHost();
804 WebContents
* RenderViewContextMenu::GetWebContents() const {
805 return source_web_contents_
;
808 Profile
* RenderViewContextMenu::GetProfile() const {
812 bool RenderViewContextMenu::AppendCustomItems() {
813 size_t total_items
= 0;
814 AddCustomItemsToMenu(params_
.custom_items
, 0, &total_items
, this,
816 return total_items
> 0;
819 void RenderViewContextMenu::AppendDeveloperItems() {
820 // Show Inspect Element in DevTools itself only in case of the debug
822 bool show_developer_items
= !IsDevToolsURL(params_
.page_url
);
824 #if defined(DEBUG_DEVTOOLS)
825 show_developer_items
= true;
828 if (!show_developer_items
)
831 // In the DevTools popup menu, "developer items" is normally the only
832 // section, so omit the separator there.
833 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
834 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTELEMENT
,
835 IDS_CONTENT_CONTEXT_INSPECTELEMENT
);
838 void RenderViewContextMenu::AppendLinkItems() {
839 if (!params_
.link_url
.is_empty()) {
840 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
,
841 IDS_CONTENT_CONTEXT_OPENLINKNEWTAB
);
842 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
,
843 IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW
);
844 if (params_
.link_url
.is_valid()) {
845 AppendProtocolHandlerSubMenu();
848 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
,
849 IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
);
850 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVELINKAS
,
851 IDS_CONTENT_CONTEXT_SAVELINKAS
);
854 menu_model_
.AddItemWithStringId(
855 IDC_CONTENT_CONTEXT_COPYLINKLOCATION
,
856 params_
.link_url
.SchemeIs(content::kMailToScheme
) ?
857 IDS_CONTENT_CONTEXT_COPYEMAILADDRESS
:
858 IDS_CONTENT_CONTEXT_COPYLINKLOCATION
);
861 void RenderViewContextMenu::AppendImageItems() {
862 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS
,
863 IDS_CONTENT_CONTEXT_SAVEIMAGEAS
);
864 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION
,
865 IDS_CONTENT_CONTEXT_COPYIMAGELOCATION
);
866 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGE
,
867 IDS_CONTENT_CONTEXT_COPYIMAGE
);
868 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
,
869 IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB
);
870 const TemplateURL
* const default_provider
=
871 TemplateURLServiceFactory::GetForProfile(profile_
)->
872 GetDefaultSearchProvider();
873 if (params_
.has_image_contents
&& default_provider
&&
874 !default_provider
->image_url().empty() &&
875 default_provider
->image_url_ref().IsValid()) {
877 IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
,
878 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
,
879 default_provider
->short_name()));
884 void RenderViewContextMenu::AppendAudioItems() {
886 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
887 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS
,
888 IDS_CONTENT_CONTEXT_SAVEAUDIOAS
);
889 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION
,
890 IDS_CONTENT_CONTEXT_COPYAUDIOLOCATION
);
891 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB
,
892 IDS_CONTENT_CONTEXT_OPENAUDIONEWTAB
);
895 void RenderViewContextMenu::AppendVideoItems() {
897 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
898 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS
,
899 IDS_CONTENT_CONTEXT_SAVEVIDEOAS
);
900 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION
,
901 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION
);
902 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB
,
903 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB
);
906 void RenderViewContextMenu::AppendMediaItems() {
907 int media_flags
= params_
.media_flags
;
909 menu_model_
.AddItemWithStringId(
910 IDC_CONTENT_CONTEXT_PLAYPAUSE
,
911 media_flags
& WebContextMenuData::MediaPaused
?
912 IDS_CONTENT_CONTEXT_PLAY
:
913 IDS_CONTENT_CONTEXT_PAUSE
);
915 menu_model_
.AddItemWithStringId(
916 IDC_CONTENT_CONTEXT_MUTE
,
917 media_flags
& WebContextMenuData::MediaMuted
?
918 IDS_CONTENT_CONTEXT_UNMUTE
:
919 IDS_CONTENT_CONTEXT_MUTE
);
921 menu_model_
.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_LOOP
,
922 IDS_CONTENT_CONTEXT_LOOP
);
923 menu_model_
.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_CONTROLS
,
924 IDS_CONTENT_CONTEXT_CONTROLS
);
927 void RenderViewContextMenu::AppendPluginItems() {
928 if (params_
.page_url
== params_
.src_url
) {
929 // Full page plugin, so show page menu items.
930 if (params_
.link_url
.is_empty() && params_
.selection_text
.empty())
933 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS
,
934 IDS_CONTENT_CONTEXT_SAVEPAGEAS
);
935 menu_model_
.AddItemWithStringId(IDC_PRINT
, IDS_CONTENT_CONTEXT_PRINT
);
938 if (params_
.media_flags
& WebContextMenuData::MediaCanRotate
) {
939 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
940 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECW
,
941 IDS_CONTENT_CONTEXT_ROTATECW
);
942 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECCW
,
943 IDS_CONTENT_CONTEXT_ROTATECCW
);
947 void RenderViewContextMenu::AppendPageItems() {
948 menu_model_
.AddItemWithStringId(IDC_BACK
, IDS_CONTENT_CONTEXT_BACK
);
949 menu_model_
.AddItemWithStringId(IDC_FORWARD
, IDS_CONTENT_CONTEXT_FORWARD
);
950 menu_model_
.AddItemWithStringId(IDC_RELOAD
, IDS_CONTENT_CONTEXT_RELOAD
);
951 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
952 menu_model_
.AddItemWithStringId(IDC_SAVE_PAGE
,
953 IDS_CONTENT_CONTEXT_SAVEPAGEAS
);
954 menu_model_
.AddItemWithStringId(IDC_PRINT
, IDS_CONTENT_CONTEXT_PRINT
);
956 if (TranslateManager::IsTranslatableURL(params_
.page_url
)) {
957 std::string locale
= g_browser_process
->GetApplicationLocale();
958 locale
= TranslateManager::GetLanguageCode(locale
);
959 base::string16 language
=
960 l10n_util::GetDisplayNameForLocale(locale
, locale
, true);
962 IDC_CONTENT_CONTEXT_TRANSLATE
,
963 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE
, language
));
966 menu_model_
.AddItemWithStringId(IDC_VIEW_SOURCE
,
967 IDS_CONTENT_CONTEXT_VIEWPAGESOURCE
);
968 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWPAGEINFO
,
969 IDS_CONTENT_CONTEXT_VIEWPAGEINFO
);
972 void RenderViewContextMenu::AppendFrameItems() {
973 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOADFRAME
,
974 IDS_CONTENT_CONTEXT_RELOADFRAME
);
975 // These two menu items have yet to be implemented.
976 // http://code.google.com/p/chromium/issues/detail?id=11827
977 // IDS_CONTENT_CONTEXT_SAVEFRAMEAS
978 // IDS_CONTENT_CONTEXT_PRINTFRAME
979 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE
,
980 IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE
);
981 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMEINFO
,
982 IDS_CONTENT_CONTEXT_VIEWFRAMEINFO
);
985 void RenderViewContextMenu::AppendCopyItem() {
986 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY
,
987 IDS_CONTENT_CONTEXT_COPY
);
990 void RenderViewContextMenu::AppendPrintItem() {
991 if (profile_
->GetPrefs()->GetBoolean(prefs::kPrintingEnabled
) &&
992 (params_
.media_type
== WebContextMenuData::MediaTypeNone
||
993 params_
.media_flags
& WebContextMenuData::MediaCanPrint
)) {
994 menu_model_
.AddItemWithStringId(IDC_PRINT
, IDS_CONTENT_CONTEXT_PRINT
);
998 void RenderViewContextMenu::AppendSearchProvider() {
1001 TrimWhitespace(params_
.selection_text
, TRIM_ALL
, ¶ms_
.selection_text
);
1002 if (params_
.selection_text
.empty())
1005 base::ReplaceChars(params_
.selection_text
, AutocompleteMatch::kInvalidChars
,
1006 base::ASCIIToUTF16(" "), ¶ms_
.selection_text
);
1008 AutocompleteMatch match
;
1009 AutocompleteClassifierFactory::GetForProfile(profile_
)->Classify(
1010 params_
.selection_text
, false, false, &match
, NULL
);
1011 selection_navigation_url_
= match
.destination_url
;
1012 if (!selection_navigation_url_
.is_valid())
1015 base::string16 printable_selection_text
= PrintableSelectionText();
1016 EscapeAmpersands(&printable_selection_text
);
1018 if (AutocompleteMatch::IsSearchType(match
.type
)) {
1019 const TemplateURL
* const default_provider
=
1020 TemplateURLServiceFactory::GetForProfile(profile_
)->
1021 GetDefaultSearchProvider();
1022 if (!default_provider
)
1024 menu_model_
.AddItem(
1025 IDC_CONTENT_CONTEXT_SEARCHWEBFOR
,
1026 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR
,
1027 default_provider
->short_name(),
1028 printable_selection_text
));
1030 if ((selection_navigation_url_
!= params_
.link_url
) &&
1031 ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1032 selection_navigation_url_
.scheme())) {
1033 menu_model_
.AddItem(
1034 IDC_CONTENT_CONTEXT_GOTOURL
,
1035 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_GOTOURL
,
1036 printable_selection_text
));
1041 void RenderViewContextMenu::AppendEditableItems() {
1042 const bool use_spellcheck_and_search
= !chrome::IsRunningInForcedAppMode();
1044 if (use_spellcheck_and_search
)
1045 AppendSpellingSuggestionsSubMenu();
1047 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO
,
1048 IDS_CONTENT_CONTEXT_UNDO
);
1049 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO
,
1050 IDS_CONTENT_CONTEXT_REDO
);
1051 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1052 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_CUT
,
1053 IDS_CONTENT_CONTEXT_CUT
);
1054 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY
,
1055 IDS_CONTENT_CONTEXT_COPY
);
1056 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE
,
1057 IDS_CONTENT_CONTEXT_PASTE
);
1058 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
,
1059 IDS_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
);
1060 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_DELETE
,
1061 IDS_CONTENT_CONTEXT_DELETE
);
1062 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1064 if (use_spellcheck_and_search
&& !params_
.keyword_url
.is_empty()) {
1065 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_ADDSEARCHENGINE
,
1066 IDS_CONTENT_CONTEXT_ADDSEARCHENGINE
);
1067 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1070 if (use_spellcheck_and_search
)
1071 AppendSpellcheckOptionsSubMenu();
1072 AppendSpeechInputOptionsSubMenu();
1073 AppendPlatformEditableItems();
1075 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1076 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SELECTALL
,
1077 IDS_CONTENT_CONTEXT_SELECTALL
);
1080 void RenderViewContextMenu::AppendSpellingSuggestionsSubMenu() {
1081 if (!spelling_menu_observer_
.get())
1082 spelling_menu_observer_
.reset(new SpellingMenuObserver(this));
1083 observers_
.AddObserver(spelling_menu_observer_
.get());
1084 spelling_menu_observer_
->InitMenu(params_
);
1087 void RenderViewContextMenu::AppendSpellcheckOptionsSubMenu() {
1088 if (!spellchecker_submenu_observer_
.get()) {
1089 spellchecker_submenu_observer_
.reset(new SpellCheckerSubMenuObserver(
1090 this, this, kSpellcheckRadioGroup
));
1092 spellchecker_submenu_observer_
->InitMenu(params_
);
1093 observers_
.AddObserver(spellchecker_submenu_observer_
.get());
1096 void RenderViewContextMenu::AppendSpeechInputOptionsSubMenu() {
1097 if (params_
.speech_input_enabled
) {
1098 speech_input_submenu_model_
.AddCheckItem(
1099 IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES
,
1100 l10n_util::GetStringUTF16(
1101 IDS_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES
));
1103 speech_input_submenu_model_
.AddItemWithStringId(
1104 IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT
,
1105 IDS_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT
);
1107 menu_model_
.AddSubMenu(
1108 IDC_SPEECH_INPUT_MENU
,
1109 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPEECH_INPUT_MENU
),
1110 &speech_input_submenu_model_
);
1114 void RenderViewContextMenu::AppendProtocolHandlerSubMenu() {
1115 const ProtocolHandlerRegistry::ProtocolHandlerList handlers
=
1116 GetHandlersForLinkUrl();
1117 if (handlers
.empty())
1119 size_t max
= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST
-
1120 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
;
1121 for (size_t i
= 0; i
< handlers
.size() && i
<= max
; i
++) {
1122 protocol_handler_submenu_model_
.AddItem(
1123 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
+ i
,
1124 handlers
[i
].title());
1126 protocol_handler_submenu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1127 protocol_handler_submenu_model_
.AddItem(
1128 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS
,
1129 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH_CONFIGURE
));
1131 menu_model_
.AddSubMenu(
1132 IDC_CONTENT_CONTEXT_OPENLINKWITH
,
1133 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH
),
1134 &protocol_handler_submenu_model_
);
1137 void RenderViewContextMenu::AppendPlatformEditableItems() {
1140 // Menu delegate functions -----------------------------------------------------
1142 bool RenderViewContextMenu::IsCommandIdEnabled(int id
) const {
1143 // If this command is is added by one of our observers, we dispatch it to the
1145 ObserverListBase
<RenderViewContextMenuObserver
>::Iterator
it(observers_
);
1146 RenderViewContextMenuObserver
* observer
;
1147 while ((observer
= it
.GetNext()) != NULL
) {
1148 if (observer
->IsCommandIdSupported(id
))
1149 return observer
->IsCommandIdEnabled(id
);
1152 CoreTabHelper
* core_tab_helper
=
1153 CoreTabHelper::FromWebContents(source_web_contents_
);
1154 int content_restrictions
= 0;
1155 if (core_tab_helper
)
1156 content_restrictions
= core_tab_helper
->content_restrictions();
1157 if (id
== IDC_PRINT
&& (content_restrictions
& CONTENT_RESTRICTION_PRINT
))
1160 if (id
== IDC_SAVE_PAGE
&&
1161 (content_restrictions
& CONTENT_RESTRICTION_SAVE
)) {
1165 // Allow Spell Check language items on sub menu for text area context menu.
1166 if ((id
>= IDC_SPELLCHECK_LANGUAGES_FIRST
) &&
1167 (id
< IDC_SPELLCHECK_LANGUAGES_LAST
)) {
1168 return profile_
->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck
);
1172 if (id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
1173 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
1174 return IsCustomItemEnabled(params_
.custom_items
, id
);
1178 if (id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
1179 id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
1180 return extension_items_
.IsCommandIdEnabled(id
);
1183 if (id
>= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
&&
1184 id
<= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST
) {
1188 IncognitoModePrefs::Availability incognito_avail
=
1189 IncognitoModePrefs::GetAvailability(profile_
->GetPrefs());
1192 return source_web_contents_
->GetController().CanGoBack();
1195 return source_web_contents_
->GetController().CanGoForward();
1198 CoreTabHelper
* core_tab_helper
=
1199 CoreTabHelper::FromWebContents(source_web_contents_
);
1200 if (!core_tab_helper
)
1203 CoreTabHelperDelegate
* core_delegate
= core_tab_helper
->delegate();
1204 return !core_delegate
||
1205 core_delegate
->CanReloadContents(source_web_contents_
);
1208 case IDC_VIEW_SOURCE
:
1209 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE
:
1210 return source_web_contents_
->GetController().CanViewSource();
1212 case IDC_CONTENT_CONTEXT_INSPECTELEMENT
:
1213 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
:
1214 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
:
1215 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP
:
1216 return IsDevCommandEnabled(id
);
1218 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO
:
1219 if (source_web_contents_
->GetController().GetActiveEntry() == NULL
)
1221 // Disabled if no browser is associated (e.g. desktop notifications).
1222 if (chrome::FindBrowserWithWebContents(source_web_contents_
) == NULL
)
1226 case IDC_CONTENT_CONTEXT_TRANSLATE
: {
1227 TranslateTabHelper
* translate_tab_helper
=
1228 TranslateTabHelper::FromWebContents(source_web_contents_
);
1229 if (!translate_tab_helper
)
1231 std::string original_lang
=
1232 translate_tab_helper
->language_state().original_language();
1233 std::string target_lang
= g_browser_process
->GetApplicationLocale();
1234 target_lang
= TranslateManager::GetLanguageCode(target_lang
);
1235 // Note that we intentionally enable the menu even if the original and
1236 // target languages are identical. This is to give a way to user to
1237 // translate a page that might contains text fragments in a different
1239 return ((params_
.edit_flags
& WebContextMenuData::CanTranslate
) != 0) &&
1240 !original_lang
.empty() && // Did we receive the page language yet?
1241 !translate_tab_helper
->language_state().IsPageTranslated() &&
1242 !source_web_contents_
->GetInterstitialPage() &&
1243 // There are some application locales which can't be used as a
1244 // target language for translation.
1245 TranslateManager::IsSupportedLanguage(target_lang
) &&
1246 // Disable on the Instant Extended NTP.
1247 !chrome::IsInstantNTP(source_web_contents_
);
1250 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
:
1251 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
:
1252 return params_
.link_url
.is_valid();
1254 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION
:
1255 return params_
.unfiltered_link_url
.is_valid();
1257 case IDC_CONTENT_CONTEXT_SAVELINKAS
: {
1258 PrefService
* local_state
= g_browser_process
->local_state();
1259 DCHECK(local_state
);
1260 // Test if file-selection dialogs are forbidden by policy.
1261 if (!local_state
->GetBoolean(prefs::kAllowFileSelectionDialogs
))
1264 return params_
.link_url
.is_valid() &&
1265 ProfileIOData::IsHandledProtocol(params_
.link_url
.scheme());
1268 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS
: {
1269 PrefService
* local_state
= g_browser_process
->local_state();
1270 DCHECK(local_state
);
1271 // Test if file-selection dialogs are forbidden by policy.
1272 if (!local_state
->GetBoolean(prefs::kAllowFileSelectionDialogs
))
1275 return params_
.src_url
.is_valid() &&
1276 ProfileIOData::IsHandledProtocol(params_
.src_url
.scheme());
1279 // The images shown in the most visited thumbnails can't be opened or
1280 // searched for conventionally.
1281 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
:
1282 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
:
1283 return params_
.src_url
.is_valid() &&
1284 (params_
.src_url
.scheme() != chrome::kChromeUIScheme
);
1286 case IDC_CONTENT_CONTEXT_COPYIMAGE
:
1287 return params_
.has_image_contents
;
1289 // Media control commands should all be disabled if the player is in an
1291 case IDC_CONTENT_CONTEXT_PLAYPAUSE
:
1292 case IDC_CONTENT_CONTEXT_LOOP
:
1293 return (params_
.media_flags
&
1294 WebContextMenuData::MediaInError
) == 0;
1296 // Mute and unmute should also be disabled if the player has no audio.
1297 case IDC_CONTENT_CONTEXT_MUTE
:
1298 return (params_
.media_flags
&
1299 WebContextMenuData::MediaHasAudio
) != 0 &&
1300 (params_
.media_flags
&
1301 WebContextMenuData::MediaInError
) == 0;
1303 // Media controls can be toggled only for video player. If we toggle
1304 // controls for audio then the player disappears, and there is no way to
1306 case IDC_CONTENT_CONTEXT_CONTROLS
:
1307 return (params_
.media_flags
&
1308 WebContextMenuData::MediaHasVideo
) != 0;
1310 case IDC_CONTENT_CONTEXT_ROTATECW
:
1311 case IDC_CONTENT_CONTEXT_ROTATECCW
:
1313 (params_
.media_flags
& WebContextMenuData::MediaCanRotate
) != 0;
1315 case IDC_CONTENT_CONTEXT_COPYAVLOCATION
:
1316 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION
:
1317 return params_
.src_url
.is_valid();
1319 case IDC_CONTENT_CONTEXT_SAVEAVAS
: {
1320 PrefService
* local_state
= g_browser_process
->local_state();
1321 DCHECK(local_state
);
1322 // Test if file-selection dialogs are forbidden by policy.
1323 if (!local_state
->GetBoolean(prefs::kAllowFileSelectionDialogs
))
1326 const GURL
& url
= params_
.src_url
;
1328 (params_
.media_flags
& WebContextMenuData::MediaCanSave
) &&
1329 url
.is_valid() && ProfileIOData::IsHandledProtocol(url
.scheme());
1330 #if defined(ENABLE_FULL_PRINTING)
1331 // Do not save the preview PDF on the print preview page.
1332 can_save
= can_save
&&
1333 !(printing::PrintPreviewDialogController::IsPrintPreviewURL(url
));
1338 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB
:
1341 case IDC_SAVE_PAGE
: {
1342 CoreTabHelper
* core_tab_helper
=
1343 CoreTabHelper::FromWebContents(source_web_contents_
);
1344 if (!core_tab_helper
)
1347 CoreTabHelperDelegate
* core_delegate
= core_tab_helper
->delegate();
1348 if (core_delegate
&&
1349 !core_delegate
->CanSaveContents(source_web_contents_
))
1352 PrefService
* local_state
= g_browser_process
->local_state();
1353 DCHECK(local_state
);
1354 // Test if file-selection dialogs are forbidden by policy.
1355 if (!local_state
->GetBoolean(prefs::kAllowFileSelectionDialogs
))
1358 // Instead of using GetURL here, we use url() (which is the "real" url of
1359 // the page) from the NavigationEntry because its reflects their origin
1360 // rather than the display one (returned by GetURL) which may be
1361 // different (like having "view-source:" on the front).
1362 // TODO(nasko): Audit all GetActiveEntry calls in this file.
1363 NavigationEntry
* active_entry
=
1364 source_web_contents_
->GetController().GetActiveEntry();
1365 return content::IsSavableURL(
1366 (active_entry
) ? active_entry
->GetURL() : GURL());
1369 case IDC_CONTENT_CONTEXT_RELOADFRAME
:
1370 return params_
.frame_url
.is_valid();
1372 case IDC_CONTENT_CONTEXT_UNDO
:
1373 return !!(params_
.edit_flags
& WebContextMenuData::CanUndo
);
1375 case IDC_CONTENT_CONTEXT_REDO
:
1376 return !!(params_
.edit_flags
& WebContextMenuData::CanRedo
);
1378 case IDC_CONTENT_CONTEXT_CUT
:
1379 return !!(params_
.edit_flags
& WebContextMenuData::CanCut
);
1381 case IDC_CONTENT_CONTEXT_COPY
:
1382 return !!(params_
.edit_flags
& WebContextMenuData::CanCopy
);
1384 case IDC_CONTENT_CONTEXT_PASTE
:
1385 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
:
1386 return !!(params_
.edit_flags
& WebContextMenuData::CanPaste
);
1388 case IDC_CONTENT_CONTEXT_DELETE
:
1389 return !!(params_
.edit_flags
& WebContextMenuData::CanDelete
);
1391 case IDC_CONTENT_CONTEXT_SELECTALL
:
1392 return !!(params_
.edit_flags
& WebContextMenuData::CanSelectAll
);
1394 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
:
1395 return !profile_
->IsOffTheRecord() && params_
.link_url
.is_valid() &&
1396 incognito_avail
!= IncognitoModePrefs::DISABLED
;
1399 return profile_
->GetPrefs()->GetBoolean(prefs::kPrintingEnabled
) &&
1400 (params_
.media_type
== WebContextMenuData::MediaTypeNone
||
1401 params_
.media_flags
& WebContextMenuData::MediaCanPrint
);
1403 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR
:
1404 case IDC_CONTENT_CONTEXT_GOTOURL
:
1405 case IDC_SPELLPANEL_TOGGLE
:
1406 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS
:
1408 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO
:
1409 // Disabled if no browser is associated (e.g. desktop notifications).
1410 if (chrome::FindBrowserWithWebContents(source_web_contents_
) == NULL
)
1414 case IDC_CHECK_SPELLING_WHILE_TYPING
:
1415 return profile_
->GetPrefs()->GetBoolean(
1416 prefs::kEnableContinuousSpellcheck
);
1418 #if !defined(OS_MACOSX) && defined(OS_POSIX)
1419 // TODO(suzhe): this should not be enabled for password fields.
1420 case IDC_INPUT_METHODS_MENU
:
1424 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE
:
1425 return !params_
.keyword_url
.is_empty();
1427 case IDC_SPELLCHECK_MENU
:
1430 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES
:
1431 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT
:
1432 case IDC_SPEECH_INPUT_MENU
:
1435 case IDC_CONTENT_CONTEXT_OPENLINKWITH
:
1438 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS
:
1447 bool RenderViewContextMenu::IsCommandIdChecked(int id
) const {
1448 // If this command is is added by one of our observers, we dispatch it to the
1450 ObserverListBase
<RenderViewContextMenuObserver
>::Iterator
it(observers_
);
1451 RenderViewContextMenuObserver
* observer
;
1452 while ((observer
= it
.GetNext()) != NULL
) {
1453 if (observer
->IsCommandIdSupported(id
))
1454 return observer
->IsCommandIdChecked(id
);
1457 // See if the video is set to looping.
1458 if (id
== IDC_CONTENT_CONTEXT_LOOP
) {
1459 return (params_
.media_flags
&
1460 WebContextMenuData::MediaLoop
) != 0;
1463 if (id
== IDC_CONTENT_CONTEXT_CONTROLS
) {
1464 return (params_
.media_flags
&
1465 WebContextMenuData::MediaControls
) != 0;
1469 if (id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
1470 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
1471 return IsCustomItemChecked(params_
.custom_items
, id
);
1475 if (id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
1476 id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
1477 return extension_items_
.IsCommandIdChecked(id
);
1480 #if defined(ENABLE_INPUT_SPEECH)
1481 // Check box for menu item 'Block offensive words'.
1482 if (id
== IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES
) {
1483 return profile_
->GetPrefs()->GetBoolean(
1484 prefs::kSpeechRecognitionFilterProfanities
);
1491 void RenderViewContextMenu::ExecuteCommand(int id
, int event_flags
) {
1492 command_executed_
= true;
1493 // If this command is is added by one of our observers, we dispatch it to the
1495 ObserverListBase
<RenderViewContextMenuObserver
>::Iterator
it(observers_
);
1496 RenderViewContextMenuObserver
* observer
;
1497 while ((observer
= it
.GetNext()) != NULL
) {
1498 if (observer
->IsCommandIdSupported(id
))
1499 return observer
->ExecuteCommand(id
);
1504 RenderViewHost
* rvh
= source_web_contents_
->GetRenderViewHost();
1506 // Process custom actions range.
1507 if (id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
1508 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
1509 unsigned action
= id
- IDC_CONTENT_CONTEXT_CUSTOM_FIRST
;
1510 const content::CustomContextMenuContext
& context
= params_
.custom_context
;
1511 #if defined(ENABLE_PLUGINS)
1512 if (context
.request_id
&& !context
.is_pepper_menu
) {
1513 ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins(
1514 source_web_contents_
, false, std::string());
1517 rvh
->ExecuteCustomContextMenuCommand(action
, context
);
1521 // Process extension menu items.
1522 if (id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
1523 id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
1524 extension_items_
.ExecuteCommand(id
, source_web_contents_
, params_
);
1528 if (id
>= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
&&
1529 id
<= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST
) {
1530 ProtocolHandlerRegistry::ProtocolHandlerList handlers
=
1531 GetHandlersForLinkUrl();
1532 if (handlers
.empty()) {
1535 content::RecordAction(
1536 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Open"));
1537 int handlerIndex
= id
- IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
;
1538 WindowOpenDisposition disposition
=
1539 ForceNewTabDispositionFromEventFlags(event_flags
);
1541 handlers
[handlerIndex
].TranslateUrl(params_
.link_url
),
1542 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
,
1545 content::PAGE_TRANSITION_LINK
);
1550 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
: {
1552 chrome::FindBrowserWithWebContents(source_web_contents_
);
1555 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
,
1557 !browser
|| browser
->is_app() ?
1558 NEW_FOREGROUND_TAB
: NEW_BACKGROUND_TAB
,
1559 content::PAGE_TRANSITION_LINK
);
1562 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
:
1565 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
,
1567 NEW_WINDOW
, content::PAGE_TRANSITION_LINK
);
1570 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
:
1571 OpenURL(params_
.link_url
,
1575 content::PAGE_TRANSITION_LINK
);
1578 case IDC_CONTENT_CONTEXT_SAVELINKAS
: {
1579 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU
);
1580 const GURL
& referrer
=
1581 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
;
1582 const GURL
& url
= params_
.link_url
;
1583 DownloadManager
* dlm
= BrowserContext::GetDownloadManager(profile_
);
1584 scoped_ptr
<DownloadUrlParameters
> dl_params(
1585 DownloadUrlParameters::FromWebContents(source_web_contents_
, url
));
1586 dl_params
->set_referrer(
1587 content::Referrer(referrer
, params_
.referrer_policy
));
1588 dl_params
->set_referrer_encoding(params_
.frame_charset
);
1589 dl_params
->set_prompt(true);
1590 dlm
->DownloadUrl(dl_params
.Pass());
1594 case IDC_CONTENT_CONTEXT_SAVEAVAS
:
1595 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS
: {
1596 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU
);
1597 const GURL
& referrer
=
1598 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
;
1599 const GURL
& url
= params_
.src_url
;
1601 if (url
== source_web_contents_
->GetURL()) {
1602 const NavigationEntry
* entry
=
1603 source_web_contents_
->GetController().GetActiveEntry();
1605 post_id
= entry
->GetPostID();
1607 DownloadManager
* dlm
= BrowserContext::GetDownloadManager(profile_
);
1608 scoped_ptr
<DownloadUrlParameters
> dl_params(
1609 DownloadUrlParameters::FromWebContents(source_web_contents_
, url
));
1610 dl_params
->set_referrer(
1611 content::Referrer(referrer
, params_
.referrer_policy
));
1612 dl_params
->set_post_id(post_id
);
1613 dl_params
->set_prefer_cache(true);
1615 dl_params
->set_method("POST");
1616 dl_params
->set_prompt(true);
1617 dlm
->DownloadUrl(dl_params
.Pass());
1621 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION
:
1622 WriteURLToClipboard(params_
.unfiltered_link_url
);
1625 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION
:
1626 case IDC_CONTENT_CONTEXT_COPYAVLOCATION
:
1627 WriteURLToClipboard(params_
.src_url
);
1630 case IDC_CONTENT_CONTEXT_COPYIMAGE
:
1631 CopyImageAt(params_
.x
, params_
.y
);
1634 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
:
1635 GetImageThumbnailForSearch();
1638 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
:
1639 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB
:
1642 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
,
1644 NEW_BACKGROUND_TAB
, content::PAGE_TRANSITION_LINK
);
1647 case IDC_CONTENT_CONTEXT_PLAYPAUSE
: {
1648 bool play
= !!(params_
.media_flags
& WebContextMenuData::MediaPaused
);
1650 content::RecordAction(UserMetricsAction("MediaContextMenu_Play"));
1652 content::RecordAction(UserMetricsAction("MediaContextMenu_Pause"));
1654 MediaPlayerActionAt(gfx::Point(params_
.x
, params_
.y
),
1655 WebMediaPlayerAction(
1656 WebMediaPlayerAction::Play
, play
));
1660 case IDC_CONTENT_CONTEXT_MUTE
: {
1661 bool mute
= !(params_
.media_flags
& WebContextMenuData::MediaMuted
);
1663 content::RecordAction(UserMetricsAction("MediaContextMenu_Mute"));
1665 content::RecordAction(UserMetricsAction("MediaContextMenu_Unmute"));
1667 MediaPlayerActionAt(gfx::Point(params_
.x
, params_
.y
),
1668 WebMediaPlayerAction(
1669 WebMediaPlayerAction::Mute
, mute
));
1673 case IDC_CONTENT_CONTEXT_LOOP
:
1674 content::RecordAction(UserMetricsAction("MediaContextMenu_Loop"));
1675 MediaPlayerActionAt(gfx::Point(params_
.x
, params_
.y
),
1676 WebMediaPlayerAction(
1677 WebMediaPlayerAction::Loop
,
1678 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP
)));
1681 case IDC_CONTENT_CONTEXT_CONTROLS
:
1682 content::RecordAction(UserMetricsAction("MediaContextMenu_Controls"));
1683 MediaPlayerActionAt(
1684 gfx::Point(params_
.x
, params_
.y
),
1685 WebMediaPlayerAction(
1686 WebMediaPlayerAction::Controls
,
1687 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS
)));
1690 case IDC_CONTENT_CONTEXT_ROTATECW
:
1691 content::RecordAction(
1692 UserMetricsAction("PluginContextMenu_RotateClockwise"));
1694 gfx::Point(params_
.x
, params_
.y
),
1696 WebPluginAction::Rotate90Clockwise
,
1700 case IDC_CONTENT_CONTEXT_ROTATECCW
:
1701 content::RecordAction(
1702 UserMetricsAction("PluginContextMenu_RotateCounterclockwise"));
1704 gfx::Point(params_
.x
, params_
.y
),
1706 WebPluginAction::Rotate90Counterclockwise
,
1711 source_web_contents_
->GetController().GoBack();
1715 source_web_contents_
->GetController().GoForward();
1719 source_web_contents_
->OnSavePage();
1723 source_web_contents_
->GetController().Reload(true);
1726 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
: {
1727 const Extension
* platform_app
= GetExtension();
1728 DCHECK(platform_app
);
1729 DCHECK(platform_app
->is_platform_app());
1731 extensions::ExtensionSystem::Get(profile_
)->extension_service()->
1732 ReloadExtension(platform_app
->id());
1736 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP
: {
1737 const Extension
* platform_app
= GetExtension();
1738 DCHECK(platform_app
);
1739 DCHECK(platform_app
->is_platform_app());
1741 apps::AppLoadService::Get(profile_
)->RestartApplication(
1742 platform_app
->id());
1747 #if defined(ENABLE_PRINTING)
1748 if (params_
.media_type
== WebContextMenuData::MediaTypeNone
) {
1749 #if defined(ENABLE_FULL_PRINTING)
1750 printing::PrintViewManager
* print_view_manager
=
1751 printing::PrintViewManager::FromWebContents(source_web_contents_
);
1753 if (!print_view_manager
)
1755 if (profile_
->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled
)) {
1756 print_view_manager
->PrintNow();
1758 print_view_manager
->PrintPreviewNow(!params_
.selection_text
.empty());
1761 printing::PrintViewManagerBasic
* print_view_manager
=
1762 printing::PrintViewManagerBasic::FromWebContents(
1763 source_web_contents_
);
1764 if (!print_view_manager
)
1766 print_view_manager
->PrintNow();
1767 #endif // defined(ENABLE_FULL_PRINTING)
1769 rvh
->Send(new PrintMsg_PrintNodeUnderContextMenu(rvh
->GetRoutingID()));
1771 #endif // defined(ENABLE_PRINTING)
1774 case IDC_VIEW_SOURCE
:
1775 source_web_contents_
->ViewSource();
1778 case IDC_CONTENT_CONTEXT_INSPECTELEMENT
:
1779 Inspect(params_
.x
, params_
.y
);
1782 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
: {
1783 const Extension
* platform_app
= GetExtension();
1784 DCHECK(platform_app
);
1785 DCHECK(platform_app
->is_platform_app());
1787 extensions::devtools_util::InspectBackgroundPage(platform_app
, profile_
);
1791 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO
: {
1792 NavigationController
* controller
= &source_web_contents_
->GetController();
1793 // Important to use GetVisibleEntry to match what's showing in the
1795 NavigationEntry
* nav_entry
= controller
->GetVisibleEntry();
1797 chrome::FindBrowserWithWebContents(source_web_contents_
);
1798 chrome::ShowWebsiteSettings(browser
, source_web_contents_
,
1799 nav_entry
->GetURL(), nav_entry
->GetSSL());
1803 case IDC_CONTENT_CONTEXT_TRANSLATE
: {
1804 // A translation might have been triggered by the time the menu got
1805 // selected, do nothing in that case.
1806 TranslateTabHelper
* translate_tab_helper
=
1807 TranslateTabHelper::FromWebContents(source_web_contents_
);
1808 if (!translate_tab_helper
||
1809 translate_tab_helper
->language_state().IsPageTranslated() ||
1810 translate_tab_helper
->language_state().translation_pending()) {
1813 std::string original_lang
=
1814 translate_tab_helper
->language_state().original_language();
1815 std::string target_lang
= g_browser_process
->GetApplicationLocale();
1816 target_lang
= TranslateManager::GetLanguageCode(target_lang
);
1817 // Since the user decided to translate for that language and site, clears
1818 // any preferences for not translating them.
1819 TranslatePrefs
prefs(profile_
->GetPrefs());
1820 prefs
.UnblockLanguage(original_lang
);
1821 prefs
.RemoveSiteFromBlacklist(params_
.page_url
.HostNoBrackets());
1822 TranslateManager::GetInstance()->TranslatePage(
1823 source_web_contents_
, original_lang
, target_lang
);
1827 case IDC_CONTENT_CONTEXT_RELOADFRAME
:
1831 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE
:
1832 source_web_contents_
->ViewFrameSource(params_
.frame_url
,
1833 params_
.frame_page_state
);
1836 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO
: {
1837 Browser
* browser
= chrome::FindBrowserWithWebContents(
1838 source_web_contents_
);
1839 chrome::ShowWebsiteSettings(browser
, source_web_contents_
,
1840 params_
.frame_url
, params_
.security_info
);
1844 case IDC_CONTENT_CONTEXT_UNDO
:
1848 case IDC_CONTENT_CONTEXT_REDO
:
1852 case IDC_CONTENT_CONTEXT_CUT
:
1856 case IDC_CONTENT_CONTEXT_COPY
:
1860 case IDC_CONTENT_CONTEXT_PASTE
:
1864 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
:
1865 rvh
->PasteAndMatchStyle();
1868 case IDC_CONTENT_CONTEXT_DELETE
:
1872 case IDC_CONTENT_CONTEXT_SELECTALL
:
1876 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR
:
1877 case IDC_CONTENT_CONTEXT_GOTOURL
: {
1878 WindowOpenDisposition disposition
=
1879 ForceNewTabDispositionFromEventFlags(event_flags
);
1880 OpenURL(selection_navigation_url_
,
1884 content::PAGE_TRANSITION_LINK
);
1887 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS
: {
1888 WindowOpenDisposition disposition
=
1889 ForceNewTabDispositionFromEventFlags(event_flags
);
1890 std::string url
= std::string(chrome::kChromeUISettingsURL
) +
1891 chrome::kLanguageOptionsSubPage
;
1892 OpenURL(GURL(url
), GURL(), 0, disposition
, content::PAGE_TRANSITION_LINK
);
1896 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS
: {
1897 content::RecordAction(
1898 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Settings"));
1899 WindowOpenDisposition disposition
=
1900 ForceNewTabDispositionFromEventFlags(event_flags
);
1901 std::string url
= std::string(chrome::kChromeUISettingsURL
) +
1902 chrome::kHandlerSettingsSubPage
;
1903 OpenURL(GURL(url
), GURL(), 0, disposition
, content::PAGE_TRANSITION_LINK
);
1907 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE
: {
1908 // Make sure the model is loaded.
1909 TemplateURLService
* model
=
1910 TemplateURLServiceFactory::GetForProfile(profile_
);
1915 SearchEngineTabHelper
* search_engine_tab_helper
=
1916 SearchEngineTabHelper::FromWebContents(source_web_contents_
);
1917 if (search_engine_tab_helper
&&
1918 search_engine_tab_helper
->delegate()) {
1919 base::string16
keyword(
1920 TemplateURLService::GenerateKeyword(params_
.page_url
));
1921 TemplateURLData data
;
1922 data
.short_name
= keyword
;
1923 data
.SetKeyword(keyword
);
1924 data
.SetURL(params_
.keyword_url
.spec());
1926 TemplateURL::GenerateFaviconURL(params_
.page_url
.GetOrigin());
1927 // Takes ownership of the TemplateURL.
1928 search_engine_tab_helper
->delegate()->
1929 ConfirmAddSearchProvider(new TemplateURL(profile_
, data
), profile_
);
1934 #if defined(ENABLE_INPUT_SPEECH)
1935 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES
: {
1936 profile_
->GetPrefs()->SetBoolean(
1937 prefs::kSpeechRecognitionFilterProfanities
,
1938 !profile_
->GetPrefs()->GetBoolean(
1939 prefs::kSpeechRecognitionFilterProfanities
));
1943 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT
: {
1944 GURL
url(chrome::kSpeechInputAboutURL
);
1945 GURL localized_url
= google_util::AppendGoogleLocaleParam(url
);
1946 // Open URL with no referrer field (because user clicked on menu item).
1947 OpenURL(localized_url
, GURL(), 0, NEW_FOREGROUND_TAB
,
1948 content::PAGE_TRANSITION_LINK
);
1958 ProtocolHandlerRegistry::ProtocolHandlerList
1959 RenderViewContextMenu::GetHandlersForLinkUrl() {
1960 ProtocolHandlerRegistry::ProtocolHandlerList handlers
=
1961 protocol_handler_registry_
->GetHandlersFor(params_
.link_url
.scheme());
1962 std::sort(handlers
.begin(), handlers
.end());
1966 void RenderViewContextMenu::MenuWillShow(ui::SimpleMenuModel
* source
) {
1967 for (int i
= 0; i
< source
->GetItemCount(); ++i
) {
1968 if (source
->IsVisibleAt(i
) &&
1969 source
->GetTypeAt(i
) != ui::MenuModel::TYPE_SEPARATOR
) {
1970 RecordShownItem(source
->GetCommandIdAt(i
));
1974 // Ignore notifications from submenus.
1975 if (source
!= &menu_model_
)
1978 content::RenderWidgetHostView
* view
=
1979 source_web_contents_
->GetRenderWidgetHostView();
1981 view
->SetShowingContextMenu(true);
1983 content::NotificationService::current()->Notify(
1984 chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN
,
1985 content::Source
<RenderViewContextMenu
>(this),
1986 content::NotificationService::NoDetails());
1989 void RenderViewContextMenu::MenuClosed(ui::SimpleMenuModel
* source
) {
1990 // Ignore notifications from submenus.
1991 if (source
!= &menu_model_
)
1994 content::RenderWidgetHostView
* view
=
1995 source_web_contents_
->GetRenderWidgetHostView();
1997 view
->SetShowingContextMenu(false);
1998 RenderViewHost
* rvh
= source_web_contents_
->GetRenderViewHost();
2000 rvh
->NotifyContextMenuClosed(params_
.custom_context
);
2003 if (!command_executed_
) {
2004 FOR_EACH_OBSERVER(RenderViewContextMenuObserver
,
2010 bool RenderViewContextMenu::IsDevCommandEnabled(int id
) const {
2011 if (id
== IDC_CONTENT_CONTEXT_INSPECTELEMENT
||
2012 id
== IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
) {
2013 const CommandLine
* command_line
= CommandLine::ForCurrentProcess();
2014 if (!profile_
->GetPrefs()->GetBoolean(prefs::kWebKitJavascriptEnabled
) ||
2015 command_line
->HasSwitch(switches::kDisableJavaScript
))
2018 // Don't enable the web inspector if the developer tools are disabled via
2019 // the preference dev-tools-disabled.
2020 if (profile_
->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled
))
2027 base::string16
RenderViewContextMenu::PrintableSelectionText() {
2028 return gfx::TruncateString(params_
.selection_text
,
2029 kMaxSelectionTextLength
);
2032 // Controller functions --------------------------------------------------------
2034 void RenderViewContextMenu::OpenURL(
2035 const GURL
& url
, const GURL
& referrer
, int64 frame_id
,
2036 WindowOpenDisposition disposition
,
2037 content::PageTransition transition
) {
2038 // Ensure that URL fragment, username and password fields are not sent
2040 GURL
sanitized_referrer(referrer
);
2041 if (sanitized_referrer
.is_valid() && (sanitized_referrer
.has_ref() ||
2042 sanitized_referrer
.has_username() || sanitized_referrer
.has_password())) {
2043 GURL::Replacements referrer_mods
;
2044 referrer_mods
.ClearRef();
2045 referrer_mods
.ClearUsername();
2046 referrer_mods
.ClearPassword();
2047 sanitized_referrer
= sanitized_referrer
.ReplaceComponents(referrer_mods
);
2050 WebContents
* new_contents
= source_web_contents_
->OpenURL(OpenURLParams(
2051 url
, content::Referrer(sanitized_referrer
, params_
.referrer_policy
),
2052 disposition
, transition
, false));
2056 RetargetingDetails details
;
2057 details
.source_web_contents
= source_web_contents_
;
2058 details
.source_frame_id
= frame_id
;
2059 details
.target_url
= url
;
2060 details
.target_web_contents
= new_contents
;
2061 details
.not_yet_in_tabstrip
= false;
2062 content::NotificationService::current()->Notify(
2063 chrome::NOTIFICATION_RETARGETING
,
2064 content::Source
<Profile
>(Profile::FromBrowserContext(
2065 source_web_contents_
->GetBrowserContext())),
2066 content::Details
<RetargetingDetails
>(&details
));
2069 void RenderViewContextMenu::CopyImageAt(int x
, int y
) {
2070 source_web_contents_
->GetRenderViewHost()->CopyImageAt(x
, y
);
2073 void RenderViewContextMenu::GetImageThumbnailForSearch() {
2074 source_web_contents_
->GetRenderViewHost()->Send(
2075 new ChromeViewMsg_RequestThumbnailForContextNode(
2076 source_web_contents_
->GetRenderViewHost()->GetRoutingID(),
2077 kImageSearchThumbnailMinSize
,
2078 gfx::Size(kImageSearchThumbnailMaxWidth
,
2079 kImageSearchThumbnailMaxHeight
)));
2082 void RenderViewContextMenu::Inspect(int x
, int y
) {
2083 content::RecordAction(UserMetricsAction("DevTools_InspectElement"));
2084 source_web_contents_
->GetRenderViewHostAtPosition(
2085 x
, y
, base::Bind(&DevToolsInspectElementAt
));
2088 void RenderViewContextMenu::WriteURLToClipboard(const GURL
& url
) {
2089 chrome_common_net::WriteURLToClipboard(
2091 profile_
->GetPrefs()->GetString(prefs::kAcceptLanguages
),
2092 ui::Clipboard::GetForCurrentThread());
2095 void RenderViewContextMenu::MediaPlayerActionAt(
2096 const gfx::Point
& location
,
2097 const WebMediaPlayerAction
& action
) {
2098 source_web_contents_
->GetRenderViewHost()->
2099 ExecuteMediaPlayerActionAtLocation(location
, action
);
2102 void RenderViewContextMenu::PluginActionAt(
2103 const gfx::Point
& location
,
2104 const WebPluginAction
& action
) {
2105 source_web_contents_
->GetRenderViewHost()->
2106 ExecutePluginActionAtLocation(location
, action
);