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.
5 #include "chrome/browser/renderer_context_menu/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_input.h"
27 #include "chrome/browser/autocomplete/autocomplete_match.h"
28 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/chrome_notification_types.h"
30 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
31 #include "chrome/browser/devtools/devtools_window.h"
32 #include "chrome/browser/download/download_service.h"
33 #include "chrome/browser/download/download_service_factory.h"
34 #include "chrome/browser/download/download_stats.h"
35 #include "chrome/browser/extensions/devtools_util.h"
36 #include "chrome/browser/extensions/extension_service.h"
37 #include "chrome/browser/google/google_util.h"
38 #include "chrome/browser/guest_view/web_view/web_view_guest.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/renderer_context_menu/context_menu_content_type_factory.h"
44 #include "chrome/browser/renderer_context_menu/spellchecker_submenu_observer.h"
45 #include "chrome/browser/renderer_context_menu/spelling_menu_observer.h"
46 #include "chrome/browser/search/search.h"
47 #include "chrome/browser/search_engines/search_terms_data.h"
48 #include "chrome/browser/search_engines/template_url.h"
49 #include "chrome/browser/search_engines/template_url_service.h"
50 #include "chrome/browser/search_engines/template_url_service_factory.h"
51 #include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
52 #include "chrome/browser/spellchecker/spellcheck_service.h"
53 #include "chrome/browser/tab_contents/retargeting_details.h"
54 #include "chrome/browser/translate/translate_service.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/chrome_pages.h"
60 #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h"
61 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
62 #include "chrome/common/chrome_constants.h"
63 #include "chrome/common/chrome_switches.h"
64 #include "chrome/common/content_restriction.h"
65 #include "chrome/common/net/url_util.h"
66 #include "chrome/common/pref_names.h"
67 #include "chrome/common/render_messages.h"
68 #include "chrome/common/spellcheck_messages.h"
69 #include "chrome/common/url_constants.h"
70 #include "components/translate/core/browser/translate_download_manager.h"
71 #include "components/translate/core/browser/translate_manager.h"
72 #include "components/translate/core/browser/translate_prefs.h"
73 #include "content/public/browser/child_process_security_policy.h"
74 #include "content/public/browser/download_manager.h"
75 #include "content/public/browser/download_save_info.h"
76 #include "content/public/browser/download_url_parameters.h"
77 #include "content/public/browser/navigation_details.h"
78 #include "content/public/browser/navigation_entry.h"
79 #include "content/public/browser/notification_service.h"
80 #include "content/public/browser/render_frame_host.h"
81 #include "content/public/browser/render_process_host.h"
82 #include "content/public/browser/render_view_host.h"
83 #include "content/public/browser/render_widget_host_view.h"
84 #include "content/public/browser/user_metrics.h"
85 #include "content/public/browser/web_contents.h"
86 #include "content/public/common/menu_item.h"
87 #include "content/public/common/ssl_status.h"
88 #include "content/public/common/url_utils.h"
89 #include "extensions/browser/extension_host.h"
90 #include "extensions/browser/extension_system.h"
91 #include "extensions/browser/view_type_utils.h"
92 #include "extensions/common/extension.h"
93 #include "grit/generated_resources.h"
94 #include "net/base/escape.h"
95 #include "third_party/WebKit/public/web/WebContextMenuData.h"
96 #include "third_party/WebKit/public/web/WebMediaPlayerAction.h"
97 #include "third_party/WebKit/public/web/WebPluginAction.h"
98 #include "ui/base/clipboard/clipboard.h"
99 #include "ui/base/l10n/l10n_util.h"
100 #include "ui/gfx/favicon_size.h"
101 #include "ui/gfx/point.h"
102 #include "ui/gfx/size.h"
103 #include "ui/gfx/text_elider.h"
105 #if defined(ENABLE_PRINTING)
106 #include "chrome/common/print_messages.h"
108 #if defined(ENABLE_FULL_PRINTING)
109 #include "chrome/browser/printing/print_preview_context_menu_observer.h"
110 #include "chrome/browser/printing/print_preview_dialog_controller.h"
111 #include "chrome/browser/printing/print_view_manager.h"
113 #include "chrome/browser/printing/print_view_manager_basic.h"
114 #endif // defined(ENABLE_FULL_PRINTING)
115 #endif // defined(ENABLE_PRINTING)
117 using base::UserMetricsAction
;
118 using blink::WebContextMenuData
;
119 using blink::WebMediaPlayerAction
;
120 using blink::WebPluginAction
;
121 using blink::WebString
;
123 using content::BrowserContext
;
124 using content::ChildProcessSecurityPolicy
;
125 using content::DownloadManager
;
126 using content::DownloadUrlParameters
;
127 using content::NavigationController
;
128 using content::NavigationEntry
;
129 using content::OpenURLParams
;
130 using content::RenderFrameHost
;
131 using content::RenderViewHost
;
132 using content::SSLStatus
;
133 using content::WebContents
;
134 using extensions::Extension
;
135 using extensions::MenuItem
;
136 using extensions::MenuManager
;
140 const int kImageSearchThumbnailMinSize
= 300 * 300;
141 const int kImageSearchThumbnailMaxWidth
= 600;
142 const int kImageSearchThumbnailMaxHeight
= 600;
144 // Maps UMA enumeration to IDC. IDC could be changed so we can't use
145 // just them and |UMA_HISTOGRAM_CUSTOM_ENUMERATION|.
146 // Never change mapping or reuse |enum_id|. Always push back new items.
147 // Items that is not used any more by |RenderViewContextMenu.ExecuteCommand|
148 // could be deleted, but don't change the rest of |kUmaEnumToControlId|.
149 const struct UmaEnumCommandIdPair
{
152 } kUmaEnumToControlId
[] = {
153 { 0, IDC_CONTENT_CONTEXT_CUSTOM_FIRST
},
154 { 1, IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
},
155 { 2, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
},
156 { 3, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
},
157 { 4, IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
},
158 { 5, IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
},
159 { 6, IDC_CONTENT_CONTEXT_SAVELINKAS
},
160 { 7, IDC_CONTENT_CONTEXT_SAVEAVAS
},
161 { 8, IDC_CONTENT_CONTEXT_SAVEIMAGEAS
},
162 { 9, IDC_CONTENT_CONTEXT_COPYLINKLOCATION
},
163 { 10, IDC_CONTENT_CONTEXT_COPYIMAGELOCATION
},
164 { 11, IDC_CONTENT_CONTEXT_COPYAVLOCATION
},
165 { 12, IDC_CONTENT_CONTEXT_COPYIMAGE
},
166 { 13, IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
},
167 { 14, IDC_CONTENT_CONTEXT_OPENAVNEWTAB
},
168 { 15, IDC_CONTENT_CONTEXT_PLAYPAUSE
},
169 { 16, IDC_CONTENT_CONTEXT_MUTE
},
170 { 17, IDC_CONTENT_CONTEXT_LOOP
},
171 { 18, IDC_CONTENT_CONTEXT_CONTROLS
},
172 { 19, IDC_CONTENT_CONTEXT_ROTATECW
},
173 { 20, IDC_CONTENT_CONTEXT_ROTATECCW
},
176 { 23, IDC_SAVE_PAGE
},
178 { 25, IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
},
179 { 26, IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP
},
181 { 28, IDC_VIEW_SOURCE
},
182 { 29, IDC_CONTENT_CONTEXT_INSPECTELEMENT
},
183 { 30, IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
},
184 { 31, IDC_CONTENT_CONTEXT_VIEWPAGEINFO
},
185 { 32, IDC_CONTENT_CONTEXT_TRANSLATE
},
186 { 33, IDC_CONTENT_CONTEXT_RELOADFRAME
},
187 { 34, IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE
},
188 { 35, IDC_CONTENT_CONTEXT_VIEWFRAMEINFO
},
189 { 36, IDC_CONTENT_CONTEXT_UNDO
},
190 { 37, IDC_CONTENT_CONTEXT_REDO
},
191 { 38, IDC_CONTENT_CONTEXT_CUT
},
192 { 39, IDC_CONTENT_CONTEXT_COPY
},
193 { 40, IDC_CONTENT_CONTEXT_PASTE
},
194 { 41, IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
},
195 { 42, IDC_CONTENT_CONTEXT_DELETE
},
196 { 43, IDC_CONTENT_CONTEXT_SELECTALL
},
197 { 44, IDC_CONTENT_CONTEXT_SEARCHWEBFOR
},
198 { 45, IDC_CONTENT_CONTEXT_GOTOURL
},
199 { 46, IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS
},
200 { 47, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS
},
201 { 48, IDC_CONTENT_CONTEXT_ADDSEARCHENGINE
},
202 { 52, IDC_CONTENT_CONTEXT_OPENLINKWITH
},
203 { 53, IDC_CHECK_SPELLING_WHILE_TYPING
},
204 { 54, IDC_SPELLCHECK_MENU
},
205 { 55, IDC_CONTENT_CONTEXT_SPELLING_TOGGLE
},
206 { 56, IDC_SPELLCHECK_LANGUAGES_FIRST
},
207 { 57, IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
},
208 { 58, IDC_SPELLCHECK_SUGGESTION_0
},
209 // Add new items here and use |enum_id| from the next line.
210 { 59, 0 }, // Must be the last. Increment |enum_id| when new IDC was added.
213 // Collapses large ranges of ids before looking for UMA enum.
214 int CollapleCommandsForUMA(int id
) {
215 if (id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
216 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
217 return IDC_CONTENT_CONTEXT_CUSTOM_FIRST
;
220 if (id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
221 id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
222 return IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
;
225 if (id
>= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
&&
226 id
<= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST
) {
227 return IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
;
230 if (id
>= IDC_SPELLCHECK_LANGUAGES_FIRST
&&
231 id
<= IDC_SPELLCHECK_LANGUAGES_LAST
) {
232 return IDC_SPELLCHECK_LANGUAGES_FIRST
;
235 if (id
>= IDC_SPELLCHECK_SUGGESTION_0
&&
236 id
<= IDC_SPELLCHECK_SUGGESTION_LAST
) {
237 return IDC_SPELLCHECK_SUGGESTION_0
;
243 // Returns UMA enum value for command specified by |id| or -1 if not found.
244 int FindUMAEnumValueForCommand(int id
) {
245 id
= CollapleCommandsForUMA(id
);
246 const size_t kMappingSize
= arraysize(kUmaEnumToControlId
);
247 for (size_t i
= 0; i
< kMappingSize
; ++i
) {
248 if (kUmaEnumToControlId
[i
].control_id
== id
) {
249 return kUmaEnumToControlId
[i
].enum_id
;
255 // Increments histogram value for used items specified by |id|.
256 void RecordUsedItem(int id
) {
257 int enum_id
= FindUMAEnumValueForCommand(id
);
259 const size_t kMappingSize
= arraysize(kUmaEnumToControlId
);
260 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Used", enum_id
,
261 kUmaEnumToControlId
[kMappingSize
- 1].enum_id
);
263 NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id
;
267 // Increments histogram value for visible context menu item specified by |id|.
268 void RecordShownItem(int id
) {
269 int enum_id
= FindUMAEnumValueForCommand(id
);
271 const size_t kMappingSize
= arraysize(kUmaEnumToControlId
);
272 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Shown", enum_id
,
273 kUmaEnumToControlId
[kMappingSize
- 1].enum_id
);
275 // Just warning here. It's harder to maintain list of all possibly
276 // visible items than executable items.
277 DLOG(ERROR
) << "Update kUmaEnumToControlId. Unhanded IDC: " << id
;
281 // Usually a new tab is expected where this function is used,
282 // however users should be able to open a tab in background
283 // or in a new window.
284 WindowOpenDisposition
ForceNewTabDispositionFromEventFlags(
286 WindowOpenDisposition disposition
=
287 ui::DispositionFromEventFlags(event_flags
);
288 return disposition
== CURRENT_TAB
? NEW_FOREGROUND_TAB
: disposition
;
291 bool IsCustomItemEnabled(const std::vector
<content::MenuItem
>& items
, int id
) {
292 DCHECK(id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
293 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
);
294 for (size_t i
= 0; i
< items
.size(); ++i
) {
295 int action_id
= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
+ items
[i
].action
;
297 return items
[i
].enabled
;
298 if (items
[i
].type
== content::MenuItem::SUBMENU
) {
299 if (IsCustomItemEnabled(items
[i
].submenu
, id
))
306 bool IsCustomItemChecked(const std::vector
<content::MenuItem
>& items
, int id
) {
307 DCHECK(id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
308 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
);
309 for (size_t i
= 0; i
< items
.size(); ++i
) {
310 int action_id
= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
+ items
[i
].action
;
312 return items
[i
].checked
;
313 if (items
[i
].type
== content::MenuItem::SUBMENU
) {
314 if (IsCustomItemChecked(items
[i
].submenu
, id
))
321 const size_t kMaxCustomMenuDepth
= 5;
322 const size_t kMaxCustomMenuTotalItems
= 1000;
324 void AddCustomItemsToMenu(const std::vector
<content::MenuItem
>& items
,
327 ui::SimpleMenuModel::Delegate
* delegate
,
328 ui::SimpleMenuModel
* menu_model
) {
329 if (depth
> kMaxCustomMenuDepth
) {
330 LOG(ERROR
) << "Custom menu too deeply nested.";
333 for (size_t i
= 0; i
< items
.size(); ++i
) {
334 if (IDC_CONTENT_CONTEXT_CUSTOM_FIRST
+ items
[i
].action
>=
335 IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
336 LOG(ERROR
) << "Custom menu action value too big.";
339 if (*total_items
>= kMaxCustomMenuTotalItems
) {
340 LOG(ERROR
) << "Custom menu too large (too many items).";
344 switch (items
[i
].type
) {
345 case content::MenuItem::OPTION
:
347 items
[i
].action
+ IDC_CONTENT_CONTEXT_CUSTOM_FIRST
,
350 case content::MenuItem::CHECKABLE_OPTION
:
351 menu_model
->AddCheckItem(
352 items
[i
].action
+ IDC_CONTENT_CONTEXT_CUSTOM_FIRST
,
355 case content::MenuItem::GROUP
:
356 // TODO(viettrungluu): I don't know what this is supposed to do.
359 case content::MenuItem::SEPARATOR
:
360 menu_model
->AddSeparator(ui::NORMAL_SEPARATOR
);
362 case content::MenuItem::SUBMENU
: {
363 ui::SimpleMenuModel
* submenu
= new ui::SimpleMenuModel(delegate
);
364 AddCustomItemsToMenu(items
[i
].submenu
, depth
+ 1, total_items
, delegate
,
366 menu_model
->AddSubMenu(
367 items
[i
].action
+ IDC_CONTENT_CONTEXT_CUSTOM_FIRST
,
379 // Helper function to escape "&" as "&&".
380 void EscapeAmpersands(base::string16
* text
) {
381 base::ReplaceChars(*text
, base::ASCIIToUTF16("&"), base::ASCIIToUTF16("&&"),
388 const size_t RenderViewContextMenu::kMaxSelectionTextLength
= 50;
391 bool RenderViewContextMenu::IsDevToolsURL(const GURL
& url
) {
392 return url
.SchemeIs(content::kChromeDevToolsScheme
);
396 bool RenderViewContextMenu::IsInternalResourcesURL(const GURL
& url
) {
397 if (!url
.SchemeIs(content::kChromeUIScheme
))
399 return url
.host() == chrome::kChromeUISyncResourcesHost
;
402 static const int kSpellcheckRadioGroup
= 1;
404 RenderViewContextMenu::RenderViewContextMenu(
405 content::RenderFrameHost
* render_frame_host
,
406 const content::ContextMenuParams
& params
)
408 source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host
)),
409 render_process_id_(render_frame_host
->GetProcess()->GetID()),
410 render_frame_id_(render_frame_host
->GetRoutingID()),
411 profile_(Profile::FromBrowserContext(
412 source_web_contents_
->GetBrowserContext())),
414 extension_items_(profile_
,
417 base::Bind(MenuItemMatchesParams
, params_
)),
418 protocol_handler_submenu_model_(this),
419 protocol_handler_registry_(
420 ProtocolHandlerRegistryFactory::GetForProfile(profile_
)),
421 command_executed_(false) {
422 content_type_
.reset(ContextMenuContentTypeFactory::Create(
423 source_web_contents_
, params
));
426 RenderViewContextMenu::~RenderViewContextMenu() {
429 // Menu construction functions -------------------------------------------------
431 void RenderViewContextMenu::Init() {
436 void RenderViewContextMenu::Cancel() {
440 static bool ExtensionPatternMatch(const extensions::URLPatternSet
& patterns
,
442 // No patterns means no restriction, so that implicitly matches.
443 if (patterns
.is_empty())
445 return patterns
.MatchesURL(url
);
449 bool RenderViewContextMenu::ExtensionContextAndPatternMatch(
450 const content::ContextMenuParams
& params
,
451 MenuItem::ContextList contexts
,
452 const extensions::URLPatternSet
& target_url_patterns
) {
453 const bool has_link
= !params
.link_url
.is_empty();
454 const bool has_selection
= !params
.selection_text
.empty();
455 const bool in_frame
= !params
.frame_url
.is_empty();
457 if (contexts
.Contains(MenuItem::ALL
) ||
458 (has_selection
&& contexts
.Contains(MenuItem::SELECTION
)) ||
459 (params
.is_editable
&& contexts
.Contains(MenuItem::EDITABLE
)) ||
460 (in_frame
&& contexts
.Contains(MenuItem::FRAME
)))
463 if (has_link
&& contexts
.Contains(MenuItem::LINK
) &&
464 ExtensionPatternMatch(target_url_patterns
, params
.link_url
))
467 switch (params
.media_type
) {
468 case WebContextMenuData::MediaTypeImage
:
469 if (contexts
.Contains(MenuItem::IMAGE
) &&
470 ExtensionPatternMatch(target_url_patterns
, params
.src_url
))
474 case WebContextMenuData::MediaTypeVideo
:
475 if (contexts
.Contains(MenuItem::VIDEO
) &&
476 ExtensionPatternMatch(target_url_patterns
, params
.src_url
))
480 case WebContextMenuData::MediaTypeAudio
:
481 if (contexts
.Contains(MenuItem::AUDIO
) &&
482 ExtensionPatternMatch(target_url_patterns
, params
.src_url
))
490 // PAGE is the least specific context, so we only examine that if none of the
491 // other contexts apply (except for FRAME, which is included in PAGE for
492 // backwards compatibility).
493 if (!has_link
&& !has_selection
&& !params
.is_editable
&&
494 params
.media_type
== WebContextMenuData::MediaTypeNone
&&
495 contexts
.Contains(MenuItem::PAGE
))
501 static const GURL
& GetDocumentURL(const content::ContextMenuParams
& params
) {
502 return params
.frame_url
.is_empty() ? params
.page_url
: params
.frame_url
;
506 bool RenderViewContextMenu::MenuItemMatchesParams(
507 const content::ContextMenuParams
& params
,
508 const extensions::MenuItem
* item
) {
509 bool match
= ExtensionContextAndPatternMatch(params
, item
->contexts(),
510 item
->target_url_patterns());
514 const GURL
& document_url
= GetDocumentURL(params
);
515 return ExtensionPatternMatch(item
->document_url_patterns(), document_url
);
518 void RenderViewContextMenu::AppendAllExtensionItems() {
519 extension_items_
.Clear();
520 ExtensionService
* service
=
521 extensions::ExtensionSystem::Get(profile_
)->extension_service();
523 return; // In unit-tests, we may not have an ExtensionService.
525 MenuManager
* menu_manager
= MenuManager::Get(profile_
);
529 base::string16 printable_selection_text
= PrintableSelectionText();
530 EscapeAmpersands(&printable_selection_text
);
532 // Get a list of extension id's that have context menu items, and sort by the
533 // top level context menu title of the extension.
534 std::set
<MenuItem::ExtensionKey
> ids
= menu_manager
->ExtensionIds();
535 std::vector
<base::string16
> sorted_menu_titles
;
536 std::map
<base::string16
, std::string
> map_ids
;
537 for (std::set
<MenuItem::ExtensionKey
>::iterator i
= ids
.begin();
540 const Extension
* extension
=
541 service
->GetExtensionById(i
->extension_id
, false);
542 // Platform apps have their context menus created directly in
543 // AppendPlatformAppItems.
544 if (extension
&& !extension
->is_platform_app()) {
545 base::string16 menu_title
= extension_items_
.GetTopLevelContextMenuTitle(
546 *i
, printable_selection_text
);
547 map_ids
[menu_title
] = i
->extension_id
;
548 sorted_menu_titles
.push_back(menu_title
);
551 if (sorted_menu_titles
.empty())
554 const std::string app_locale
= g_browser_process
->GetApplicationLocale();
555 l10n_util::SortStrings16(app_locale
, &sorted_menu_titles
);
558 base::TimeTicks begin
= base::TimeTicks::Now();
559 for (size_t i
= 0; i
< sorted_menu_titles
.size(); ++i
) {
560 const std::string
& id
= map_ids
[sorted_menu_titles
[i
]];
561 const MenuItem::ExtensionKey
extension_key(id
);
562 extension_items_
.AppendExtensionItems(
563 extension_key
, printable_selection_text
, &index
);
566 UMA_HISTOGRAM_TIMES("Extensions.ContextMenus_BuildTime",
567 base::TimeTicks::Now() - begin
);
568 UMA_HISTOGRAM_COUNTS("Extensions.ContextMenus_ItemCount", index
);
571 void RenderViewContextMenu::AppendCurrentExtensionItems() {
572 // Avoid appending extension related items when |extension| is null.
573 // For Panel, this happens when the panel is navigated to a url outside of the
574 // extension's package.
575 const Extension
* extension
= GetExtension();
577 // Only add extension items from this extension.
579 const MenuItem::ExtensionKey
key(
580 extension
->id(), WebViewGuest::GetViewInstanceId(source_web_contents_
));
581 extension_items_
.AppendExtensionItems(
582 key
, PrintableSelectionText(), &index
);
586 void RenderViewContextMenu::InitMenu() {
587 if (content_type_
->SupportsGroup(ContextMenuContentType::ITEM_GROUP_CUSTOM
)) {
590 const bool has_selection
= !params_
.selection_text
.empty();
592 // We will add more items if there's a selection, so add a separator.
593 // TODO(lazyboy): Clean up separator logic.
594 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
598 if (content_type_
->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PAGE
))
601 if (content_type_
->SupportsGroup(ContextMenuContentType::ITEM_GROUP_FRAME
)) {
602 // Merge in frame items with page items if we clicked within a frame that
604 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
608 if (content_type_
->SupportsGroup(ContextMenuContentType::ITEM_GROUP_LINK
)) {
610 if (params_
.media_type
!= WebContextMenuData::MediaTypeNone
)
611 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
614 if (content_type_
->SupportsGroup(
615 ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE
)) {
619 if (content_type_
->SupportsGroup(
620 ContextMenuContentType::ITEM_GROUP_SEARCHWEBFORIMAGE
)) {
621 AppendSearchWebForImageItems();
624 if (content_type_
->SupportsGroup(
625 ContextMenuContentType::ITEM_GROUP_MEDIA_VIDEO
)) {
629 if (content_type_
->SupportsGroup(
630 ContextMenuContentType::ITEM_GROUP_MEDIA_AUDIO
)) {
634 if (content_type_
->SupportsGroup(
635 ContextMenuContentType::ITEM_GROUP_MEDIA_CANVAS
)) {
639 if (content_type_
->SupportsGroup(
640 ContextMenuContentType::ITEM_GROUP_MEDIA_PLUGIN
)) {
644 // ITEM_GROUP_MEDIA_FILE has no specific items.
646 if (content_type_
->SupportsGroup(ContextMenuContentType::ITEM_GROUP_EDITABLE
))
647 AppendEditableItems();
649 if (content_type_
->SupportsGroup(ContextMenuContentType::ITEM_GROUP_COPY
)) {
650 DCHECK(!content_type_
->SupportsGroup(
651 ContextMenuContentType::ITEM_GROUP_EDITABLE
));
655 if (content_type_
->SupportsGroup(
656 ContextMenuContentType::ITEM_GROUP_SEARCH_PROVIDER
)) {
657 AppendSearchProvider();
660 if (content_type_
->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PRINT
))
663 if (content_type_
->SupportsGroup(
664 ContextMenuContentType::ITEM_GROUP_ALL_EXTENSION
)) {
665 DCHECK(!content_type_
->SupportsGroup(
666 ContextMenuContentType::ITEM_GROUP_CURRENT_EXTENSION
));
667 AppendAllExtensionItems();
670 if (content_type_
->SupportsGroup(
671 ContextMenuContentType::ITEM_GROUP_CURRENT_EXTENSION
)) {
672 DCHECK(!content_type_
->SupportsGroup(
673 ContextMenuContentType::ITEM_GROUP_ALL_EXTENSION
));
674 AppendCurrentExtensionItems();
677 if (content_type_
->SupportsGroup(
678 ContextMenuContentType::ITEM_GROUP_DEVELOPER
)) {
679 AppendDeveloperItems();
682 if (content_type_
->SupportsGroup(
683 ContextMenuContentType::ITEM_GROUP_DEVTOOLS_UNPACKED_EXT
)) {
684 AppendDevtoolsForUnpackedExtensions();
687 if (content_type_
->SupportsGroup(
688 ContextMenuContentType::ITEM_GROUP_PRINT_PREVIEW
)) {
689 AppendPrintPreviewItems();
693 void RenderViewContextMenu::AppendPrintPreviewItems() {
694 #if defined(ENABLE_FULL_PRINTING)
695 if (!print_preview_menu_observer_
.get()) {
696 print_preview_menu_observer_
.reset(
697 new PrintPreviewContextMenuObserver(source_web_contents_
));
700 observers_
.AddObserver(print_preview_menu_observer_
.get());
704 const Extension
* RenderViewContextMenu::GetExtension() const {
705 extensions::ExtensionSystem
* system
=
706 extensions::ExtensionSystem::Get(profile_
);
707 // There is no process manager in some tests.
708 if (!system
->process_manager())
711 return system
->process_manager()->GetExtensionForRenderViewHost(
712 source_web_contents_
->GetRenderViewHost());
715 void RenderViewContextMenu::AddMenuItem(int command_id
,
716 const base::string16
& title
) {
717 menu_model_
.AddItem(command_id
, title
);
720 void RenderViewContextMenu::AddCheckItem(int command_id
,
721 const base::string16
& title
) {
722 menu_model_
.AddCheckItem(command_id
, title
);
725 void RenderViewContextMenu::AddSeparator() {
726 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
729 void RenderViewContextMenu::AddSubMenu(int command_id
,
730 const base::string16
& label
,
731 ui::MenuModel
* model
) {
732 menu_model_
.AddSubMenu(command_id
, label
, model
);
735 void RenderViewContextMenu::UpdateMenuItem(int command_id
,
738 const base::string16
& label
) {
739 // This function needs platform-specific implementation.
743 RenderViewHost
* RenderViewContextMenu::GetRenderViewHost() const {
744 return source_web_contents_
->GetRenderViewHost();
747 WebContents
* RenderViewContextMenu::GetWebContents() const {
748 return source_web_contents_
;
751 Profile
* RenderViewContextMenu::GetProfile() const {
755 bool RenderViewContextMenu::AppendCustomItems() {
756 size_t total_items
= 0;
757 AddCustomItemsToMenu(params_
.custom_items
, 0, &total_items
, this,
759 return total_items
> 0;
762 void RenderViewContextMenu::AppendDeveloperItems() {
763 // Show Inspect Element in DevTools itself only in case of the debug
765 bool show_developer_items
= !IsDevToolsURL(params_
.page_url
);
767 #if defined(DEBUG_DEVTOOLS)
768 show_developer_items
= true;
771 if (!show_developer_items
)
774 // In the DevTools popup menu, "developer items" is normally the only
775 // section, so omit the separator there.
776 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
777 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTELEMENT
,
778 IDS_CONTENT_CONTEXT_INSPECTELEMENT
);
781 void RenderViewContextMenu::AppendDevtoolsForUnpackedExtensions() {
782 // Add a separator if there are any items already in the menu.
783 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
785 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
,
786 IDS_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
);
787 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP
,
788 IDS_CONTENT_CONTEXT_RESTART_APP
);
789 AppendDeveloperItems();
790 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
,
791 IDS_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
);
794 void RenderViewContextMenu::AppendLinkItems() {
795 if (!params_
.link_url
.is_empty()) {
796 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
,
797 IDS_CONTENT_CONTEXT_OPENLINKNEWTAB
);
798 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
,
799 IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW
);
800 if (params_
.link_url
.is_valid()) {
801 AppendProtocolHandlerSubMenu();
804 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
,
805 IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
);
806 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVELINKAS
,
807 IDS_CONTENT_CONTEXT_SAVELINKAS
);
810 menu_model_
.AddItemWithStringId(
811 IDC_CONTENT_CONTEXT_COPYLINKLOCATION
,
812 params_
.link_url
.SchemeIs(url::kMailToScheme
) ?
813 IDS_CONTENT_CONTEXT_COPYEMAILADDRESS
:
814 IDS_CONTENT_CONTEXT_COPYLINKLOCATION
);
817 void RenderViewContextMenu::AppendImageItems() {
818 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS
,
819 IDS_CONTENT_CONTEXT_SAVEIMAGEAS
);
820 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION
,
821 IDS_CONTENT_CONTEXT_COPYIMAGELOCATION
);
822 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGE
,
823 IDS_CONTENT_CONTEXT_COPYIMAGE
);
824 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
,
825 IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB
);
828 void RenderViewContextMenu::AppendSearchWebForImageItems() {
829 const TemplateURL
* const default_provider
=
830 TemplateURLServiceFactory::GetForProfile(profile_
)->
831 GetDefaultSearchProvider();
832 if (params_
.has_image_contents
&& default_provider
&&
833 !default_provider
->image_url().empty() &&
834 default_provider
->image_url_ref().IsValid()) {
836 IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
,
837 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
,
838 default_provider
->short_name()));
842 void RenderViewContextMenu::AppendAudioItems() {
844 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
845 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS
,
846 IDS_CONTENT_CONTEXT_SAVEAUDIOAS
);
847 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION
,
848 IDS_CONTENT_CONTEXT_COPYAUDIOLOCATION
);
849 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB
,
850 IDS_CONTENT_CONTEXT_OPENAUDIONEWTAB
);
853 void RenderViewContextMenu::AppendCanvasItems() {
854 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS
,
855 IDS_CONTENT_CONTEXT_SAVEIMAGEAS
);
857 // TODO(zino): We should support 'copy image' for canvas.
858 // http://crbug.com/369092
861 void RenderViewContextMenu::AppendVideoItems() {
863 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
864 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS
,
865 IDS_CONTENT_CONTEXT_SAVEVIDEOAS
);
866 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION
,
867 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION
);
868 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB
,
869 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB
);
872 void RenderViewContextMenu::AppendMediaItems() {
873 int media_flags
= params_
.media_flags
;
875 menu_model_
.AddItemWithStringId(
876 IDC_CONTENT_CONTEXT_PLAYPAUSE
,
877 media_flags
& WebContextMenuData::MediaPaused
?
878 IDS_CONTENT_CONTEXT_PLAY
:
879 IDS_CONTENT_CONTEXT_PAUSE
);
881 menu_model_
.AddItemWithStringId(
882 IDC_CONTENT_CONTEXT_MUTE
,
883 media_flags
& WebContextMenuData::MediaMuted
?
884 IDS_CONTENT_CONTEXT_UNMUTE
:
885 IDS_CONTENT_CONTEXT_MUTE
);
887 menu_model_
.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_LOOP
,
888 IDS_CONTENT_CONTEXT_LOOP
);
889 menu_model_
.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_CONTROLS
,
890 IDS_CONTENT_CONTEXT_CONTROLS
);
893 void RenderViewContextMenu::AppendPluginItems() {
894 if (params_
.page_url
== params_
.src_url
) {
895 // Full page plugin, so show page menu items.
896 if (params_
.link_url
.is_empty() && params_
.selection_text
.empty())
899 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS
,
900 IDS_CONTENT_CONTEXT_SAVEPAGEAS
);
901 menu_model_
.AddItemWithStringId(IDC_PRINT
, IDS_CONTENT_CONTEXT_PRINT
);
904 if (params_
.media_flags
& WebContextMenuData::MediaCanRotate
) {
905 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
906 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECW
,
907 IDS_CONTENT_CONTEXT_ROTATECW
);
908 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECCW
,
909 IDS_CONTENT_CONTEXT_ROTATECCW
);
913 void RenderViewContextMenu::AppendPageItems() {
914 menu_model_
.AddItemWithStringId(IDC_BACK
, IDS_CONTENT_CONTEXT_BACK
);
915 menu_model_
.AddItemWithStringId(IDC_FORWARD
, IDS_CONTENT_CONTEXT_FORWARD
);
916 menu_model_
.AddItemWithStringId(IDC_RELOAD
, IDS_CONTENT_CONTEXT_RELOAD
);
917 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
918 menu_model_
.AddItemWithStringId(IDC_SAVE_PAGE
,
919 IDS_CONTENT_CONTEXT_SAVEPAGEAS
);
920 menu_model_
.AddItemWithStringId(IDC_PRINT
, IDS_CONTENT_CONTEXT_PRINT
);
922 if (TranslateService::IsTranslatableURL(params_
.page_url
)) {
923 std::string locale
= g_browser_process
->GetApplicationLocale();
924 locale
= TranslateDownloadManager::GetLanguageCode(locale
);
925 base::string16 language
=
926 l10n_util::GetDisplayNameForLocale(locale
, locale
, true);
928 IDC_CONTENT_CONTEXT_TRANSLATE
,
929 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE
, language
));
932 menu_model_
.AddItemWithStringId(IDC_VIEW_SOURCE
,
933 IDS_CONTENT_CONTEXT_VIEWPAGESOURCE
);
934 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWPAGEINFO
,
935 IDS_CONTENT_CONTEXT_VIEWPAGEINFO
);
938 void RenderViewContextMenu::AppendFrameItems() {
939 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOADFRAME
,
940 IDS_CONTENT_CONTEXT_RELOADFRAME
);
941 // These two menu items have yet to be implemented.
942 // http://code.google.com/p/chromium/issues/detail?id=11827
943 // IDS_CONTENT_CONTEXT_SAVEFRAMEAS
944 // IDS_CONTENT_CONTEXT_PRINTFRAME
945 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE
,
946 IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE
);
947 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMEINFO
,
948 IDS_CONTENT_CONTEXT_VIEWFRAMEINFO
);
951 void RenderViewContextMenu::AppendCopyItem() {
952 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY
,
953 IDS_CONTENT_CONTEXT_COPY
);
956 void RenderViewContextMenu::AppendPrintItem() {
957 if (profile_
->GetPrefs()->GetBoolean(prefs::kPrintingEnabled
) &&
958 (params_
.media_type
== WebContextMenuData::MediaTypeNone
||
959 params_
.media_flags
& WebContextMenuData::MediaCanPrint
)) {
960 menu_model_
.AddItemWithStringId(IDC_PRINT
, IDS_CONTENT_CONTEXT_PRINT
);
964 void RenderViewContextMenu::AppendSearchProvider() {
967 base::TrimWhitespace(params_
.selection_text
, base::TRIM_ALL
,
968 ¶ms_
.selection_text
);
969 if (params_
.selection_text
.empty())
972 base::ReplaceChars(params_
.selection_text
, AutocompleteMatch::kInvalidChars
,
973 base::ASCIIToUTF16(" "), ¶ms_
.selection_text
);
975 AutocompleteMatch match
;
976 AutocompleteClassifierFactory::GetForProfile(profile_
)->Classify(
977 params_
.selection_text
, false, false, AutocompleteInput::INVALID_SPEC
,
979 selection_navigation_url_
= match
.destination_url
;
980 if (!selection_navigation_url_
.is_valid())
983 base::string16 printable_selection_text
= PrintableSelectionText();
984 EscapeAmpersands(&printable_selection_text
);
986 if (AutocompleteMatch::IsSearchType(match
.type
)) {
987 const TemplateURL
* const default_provider
=
988 TemplateURLServiceFactory::GetForProfile(profile_
)->
989 GetDefaultSearchProvider();
990 if (!default_provider
)
993 IDC_CONTENT_CONTEXT_SEARCHWEBFOR
,
994 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR
,
995 default_provider
->short_name(),
996 printable_selection_text
));
998 if ((selection_navigation_url_
!= params_
.link_url
) &&
999 ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1000 selection_navigation_url_
.scheme())) {
1001 menu_model_
.AddItem(
1002 IDC_CONTENT_CONTEXT_GOTOURL
,
1003 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_GOTOURL
,
1004 printable_selection_text
));
1009 void RenderViewContextMenu::AppendEditableItems() {
1010 const bool use_spellcheck_and_search
= !chrome::IsRunningInForcedAppMode();
1012 if (use_spellcheck_and_search
)
1013 AppendSpellingSuggestionsSubMenu();
1015 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO
,
1016 IDS_CONTENT_CONTEXT_UNDO
);
1017 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO
,
1018 IDS_CONTENT_CONTEXT_REDO
);
1019 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1020 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_CUT
,
1021 IDS_CONTENT_CONTEXT_CUT
);
1022 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY
,
1023 IDS_CONTENT_CONTEXT_COPY
);
1024 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE
,
1025 IDS_CONTENT_CONTEXT_PASTE
);
1026 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
,
1027 IDS_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
);
1028 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_DELETE
,
1029 IDS_CONTENT_CONTEXT_DELETE
);
1030 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1032 if (use_spellcheck_and_search
&& !params_
.keyword_url
.is_empty()) {
1033 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_ADDSEARCHENGINE
,
1034 IDS_CONTENT_CONTEXT_ADDSEARCHENGINE
);
1035 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1038 if (use_spellcheck_and_search
)
1039 AppendSpellcheckOptionsSubMenu();
1040 AppendPlatformEditableItems();
1042 menu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1043 menu_model_
.AddItemWithStringId(IDC_CONTENT_CONTEXT_SELECTALL
,
1044 IDS_CONTENT_CONTEXT_SELECTALL
);
1047 void RenderViewContextMenu::AppendSpellingSuggestionsSubMenu() {
1048 if (!spelling_menu_observer_
.get())
1049 spelling_menu_observer_
.reset(new SpellingMenuObserver(this));
1050 observers_
.AddObserver(spelling_menu_observer_
.get());
1051 spelling_menu_observer_
->InitMenu(params_
);
1054 void RenderViewContextMenu::AppendSpellcheckOptionsSubMenu() {
1055 if (!spellchecker_submenu_observer_
.get()) {
1056 spellchecker_submenu_observer_
.reset(new SpellCheckerSubMenuObserver(
1057 this, this, kSpellcheckRadioGroup
));
1059 spellchecker_submenu_observer_
->InitMenu(params_
);
1060 observers_
.AddObserver(spellchecker_submenu_observer_
.get());
1063 void RenderViewContextMenu::AppendProtocolHandlerSubMenu() {
1064 const ProtocolHandlerRegistry::ProtocolHandlerList handlers
=
1065 GetHandlersForLinkUrl();
1066 if (handlers
.empty())
1068 size_t max
= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST
-
1069 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
;
1070 for (size_t i
= 0; i
< handlers
.size() && i
<= max
; i
++) {
1071 protocol_handler_submenu_model_
.AddItem(
1072 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
+ i
,
1073 base::UTF8ToUTF16(handlers
[i
].url().host()));
1075 protocol_handler_submenu_model_
.AddSeparator(ui::NORMAL_SEPARATOR
);
1076 protocol_handler_submenu_model_
.AddItem(
1077 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS
,
1078 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH_CONFIGURE
));
1080 menu_model_
.AddSubMenu(
1081 IDC_CONTENT_CONTEXT_OPENLINKWITH
,
1082 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH
),
1083 &protocol_handler_submenu_model_
);
1086 void RenderViewContextMenu::AppendPlatformEditableItems() {
1089 // Menu delegate functions -----------------------------------------------------
1091 bool RenderViewContextMenu::IsCommandIdEnabled(int id
) const {
1092 // If this command is is added by one of our observers, we dispatch it to the
1094 ObserverListBase
<RenderViewContextMenuObserver
>::Iterator
it(observers_
);
1095 RenderViewContextMenuObserver
* observer
;
1096 while ((observer
= it
.GetNext()) != NULL
) {
1097 if (observer
->IsCommandIdSupported(id
))
1098 return observer
->IsCommandIdEnabled(id
);
1101 CoreTabHelper
* core_tab_helper
=
1102 CoreTabHelper::FromWebContents(source_web_contents_
);
1103 int content_restrictions
= 0;
1104 if (core_tab_helper
)
1105 content_restrictions
= core_tab_helper
->content_restrictions();
1106 if (id
== IDC_PRINT
&& (content_restrictions
& CONTENT_RESTRICTION_PRINT
))
1109 if (id
== IDC_SAVE_PAGE
&&
1110 (content_restrictions
& CONTENT_RESTRICTION_SAVE
)) {
1114 // Allow Spell Check language items on sub menu for text area context menu.
1115 if ((id
>= IDC_SPELLCHECK_LANGUAGES_FIRST
) &&
1116 (id
< IDC_SPELLCHECK_LANGUAGES_LAST
)) {
1117 return profile_
->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck
);
1121 if (id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
1122 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
1123 return IsCustomItemEnabled(params_
.custom_items
, id
);
1127 if (id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
1128 id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
1129 return extension_items_
.IsCommandIdEnabled(id
);
1132 if (id
>= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
&&
1133 id
<= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST
) {
1137 IncognitoModePrefs::Availability incognito_avail
=
1138 IncognitoModePrefs::GetAvailability(profile_
->GetPrefs());
1141 return source_web_contents_
->GetController().CanGoBack();
1144 return source_web_contents_
->GetController().CanGoForward();
1147 CoreTabHelper
* core_tab_helper
=
1148 CoreTabHelper::FromWebContents(source_web_contents_
);
1149 if (!core_tab_helper
)
1152 CoreTabHelperDelegate
* core_delegate
= core_tab_helper
->delegate();
1153 return !core_delegate
||
1154 core_delegate
->CanReloadContents(source_web_contents_
);
1157 case IDC_VIEW_SOURCE
:
1158 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE
:
1159 return source_web_contents_
->GetController().CanViewSource();
1161 case IDC_CONTENT_CONTEXT_INSPECTELEMENT
:
1162 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
:
1163 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
:
1164 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP
:
1165 return IsDevCommandEnabled(id
);
1167 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO
:
1168 if (source_web_contents_
->GetController().GetVisibleEntry() == NULL
)
1170 // Disabled if no browser is associated (e.g. desktop notifications).
1171 if (chrome::FindBrowserWithWebContents(source_web_contents_
) == NULL
)
1175 case IDC_CONTENT_CONTEXT_TRANSLATE
: {
1176 TranslateTabHelper
* translate_tab_helper
=
1177 TranslateTabHelper::FromWebContents(source_web_contents_
);
1178 if (!translate_tab_helper
)
1180 std::string original_lang
=
1181 translate_tab_helper
->GetLanguageState().original_language();
1182 std::string target_lang
= g_browser_process
->GetApplicationLocale();
1183 target_lang
= TranslateDownloadManager::GetLanguageCode(target_lang
);
1184 // Note that we intentionally enable the menu even if the original and
1185 // target languages are identical. This is to give a way to user to
1186 // translate a page that might contains text fragments in a different
1188 return ((params_
.edit_flags
& WebContextMenuData::CanTranslate
) != 0) &&
1189 !original_lang
.empty() && // Did we receive the page language yet?
1190 !translate_tab_helper
->GetLanguageState().IsPageTranslated() &&
1191 !source_web_contents_
->GetInterstitialPage() &&
1192 // There are some application locales which can't be used as a
1193 // target language for translation.
1194 TranslateDownloadManager::IsSupportedLanguage(target_lang
) &&
1195 // Disable on the Instant Extended NTP.
1196 !chrome::IsInstantNTP(source_web_contents_
);
1199 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
:
1200 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
:
1201 return params_
.link_url
.is_valid();
1203 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION
:
1204 return params_
.unfiltered_link_url
.is_valid();
1206 case IDC_CONTENT_CONTEXT_SAVELINKAS
: {
1207 PrefService
* local_state
= g_browser_process
->local_state();
1208 DCHECK(local_state
);
1209 // Test if file-selection dialogs are forbidden by policy.
1210 if (!local_state
->GetBoolean(prefs::kAllowFileSelectionDialogs
))
1213 return params_
.link_url
.is_valid() &&
1214 ProfileIOData::IsHandledProtocol(params_
.link_url
.scheme());
1217 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS
: {
1218 PrefService
* local_state
= g_browser_process
->local_state();
1219 DCHECK(local_state
);
1220 // Test if file-selection dialogs are forbidden by policy.
1221 if (!local_state
->GetBoolean(prefs::kAllowFileSelectionDialogs
))
1224 if (params_
.media_type
== WebContextMenuData::MediaTypeCanvas
)
1227 return params_
.src_url
.is_valid() &&
1228 ProfileIOData::IsHandledProtocol(params_
.src_url
.scheme());
1231 // The images shown in the most visited thumbnails can't be opened or
1232 // searched for conventionally.
1233 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
:
1234 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
:
1235 return params_
.src_url
.is_valid() &&
1236 (params_
.src_url
.scheme() != content::kChromeUIScheme
);
1238 case IDC_CONTENT_CONTEXT_COPYIMAGE
:
1239 return params_
.has_image_contents
;
1241 // Media control commands should all be disabled if the player is in an
1243 case IDC_CONTENT_CONTEXT_PLAYPAUSE
:
1244 case IDC_CONTENT_CONTEXT_LOOP
:
1245 return (params_
.media_flags
&
1246 WebContextMenuData::MediaInError
) == 0;
1248 // Mute and unmute should also be disabled if the player has no audio.
1249 case IDC_CONTENT_CONTEXT_MUTE
:
1250 return (params_
.media_flags
&
1251 WebContextMenuData::MediaHasAudio
) != 0 &&
1252 (params_
.media_flags
&
1253 WebContextMenuData::MediaInError
) == 0;
1255 case IDC_CONTENT_CONTEXT_CONTROLS
:
1256 return (params_
.media_flags
&
1257 WebContextMenuData::MediaCanToggleControls
) != 0;
1259 case IDC_CONTENT_CONTEXT_ROTATECW
:
1260 case IDC_CONTENT_CONTEXT_ROTATECCW
:
1262 (params_
.media_flags
& WebContextMenuData::MediaCanRotate
) != 0;
1264 case IDC_CONTENT_CONTEXT_COPYAVLOCATION
:
1265 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION
:
1266 return params_
.src_url
.is_valid();
1268 case IDC_CONTENT_CONTEXT_SAVEAVAS
: {
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 const GURL
& url
= params_
.src_url
;
1277 (params_
.media_flags
& WebContextMenuData::MediaCanSave
) &&
1278 url
.is_valid() && ProfileIOData::IsHandledProtocol(url
.scheme());
1279 #if defined(ENABLE_FULL_PRINTING)
1280 // Do not save the preview PDF on the print preview page.
1281 can_save
= can_save
&&
1282 !(printing::PrintPreviewDialogController::IsPrintPreviewURL(url
));
1287 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB
:
1290 case IDC_SAVE_PAGE
: {
1291 CoreTabHelper
* core_tab_helper
=
1292 CoreTabHelper::FromWebContents(source_web_contents_
);
1293 if (!core_tab_helper
)
1296 CoreTabHelperDelegate
* core_delegate
= core_tab_helper
->delegate();
1297 if (core_delegate
&&
1298 !core_delegate
->CanSaveContents(source_web_contents_
))
1301 PrefService
* local_state
= g_browser_process
->local_state();
1302 DCHECK(local_state
);
1303 // Test if file-selection dialogs are forbidden by policy.
1304 if (!local_state
->GetBoolean(prefs::kAllowFileSelectionDialogs
))
1307 // We save the last committed entry (which the user is looking at), as
1308 // opposed to any pending URL that hasn't committed yet.
1309 NavigationEntry
* entry
=
1310 source_web_contents_
->GetController().GetLastCommittedEntry();
1311 return content::IsSavableURL(entry
? entry
->GetURL() : GURL());
1314 case IDC_CONTENT_CONTEXT_RELOADFRAME
:
1315 return params_
.frame_url
.is_valid();
1317 case IDC_CONTENT_CONTEXT_UNDO
:
1318 return !!(params_
.edit_flags
& WebContextMenuData::CanUndo
);
1320 case IDC_CONTENT_CONTEXT_REDO
:
1321 return !!(params_
.edit_flags
& WebContextMenuData::CanRedo
);
1323 case IDC_CONTENT_CONTEXT_CUT
:
1324 return !!(params_
.edit_flags
& WebContextMenuData::CanCut
);
1326 case IDC_CONTENT_CONTEXT_COPY
:
1327 return !!(params_
.edit_flags
& WebContextMenuData::CanCopy
);
1329 case IDC_CONTENT_CONTEXT_PASTE
:
1330 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
:
1331 return !!(params_
.edit_flags
& WebContextMenuData::CanPaste
);
1333 case IDC_CONTENT_CONTEXT_DELETE
:
1334 return !!(params_
.edit_flags
& WebContextMenuData::CanDelete
);
1336 case IDC_CONTENT_CONTEXT_SELECTALL
:
1337 return !!(params_
.edit_flags
& WebContextMenuData::CanSelectAll
);
1339 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
:
1340 return !profile_
->IsOffTheRecord() && params_
.link_url
.is_valid() &&
1341 incognito_avail
!= IncognitoModePrefs::DISABLED
;
1344 return profile_
->GetPrefs()->GetBoolean(prefs::kPrintingEnabled
) &&
1345 (params_
.media_type
== WebContextMenuData::MediaTypeNone
||
1346 params_
.media_flags
& WebContextMenuData::MediaCanPrint
);
1348 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR
:
1349 case IDC_CONTENT_CONTEXT_GOTOURL
:
1350 case IDC_SPELLPANEL_TOGGLE
:
1351 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS
:
1353 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO
:
1354 // Disabled if no browser is associated (e.g. desktop notifications).
1355 if (chrome::FindBrowserWithWebContents(source_web_contents_
) == NULL
)
1359 case IDC_CHECK_SPELLING_WHILE_TYPING
:
1360 return profile_
->GetPrefs()->GetBoolean(
1361 prefs::kEnableContinuousSpellcheck
);
1363 #if !defined(OS_MACOSX) && defined(OS_POSIX)
1364 // TODO(suzhe): this should not be enabled for password fields.
1365 case IDC_INPUT_METHODS_MENU
:
1369 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE
:
1370 return !params_
.keyword_url
.is_empty();
1372 case IDC_SPELLCHECK_MENU
:
1375 case IDC_CONTENT_CONTEXT_OPENLINKWITH
:
1378 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS
:
1387 bool RenderViewContextMenu::IsCommandIdChecked(int id
) const {
1388 // If this command is is added by one of our observers, we dispatch it to the
1390 ObserverListBase
<RenderViewContextMenuObserver
>::Iterator
it(observers_
);
1391 RenderViewContextMenuObserver
* observer
;
1392 while ((observer
= it
.GetNext()) != NULL
) {
1393 if (observer
->IsCommandIdSupported(id
))
1394 return observer
->IsCommandIdChecked(id
);
1397 // See if the video is set to looping.
1398 if (id
== IDC_CONTENT_CONTEXT_LOOP
) {
1399 return (params_
.media_flags
&
1400 WebContextMenuData::MediaLoop
) != 0;
1403 if (id
== IDC_CONTENT_CONTEXT_CONTROLS
) {
1404 return (params_
.media_flags
&
1405 WebContextMenuData::MediaControls
) != 0;
1409 if (id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
1410 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
1411 return IsCustomItemChecked(params_
.custom_items
, id
);
1415 if (id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
1416 id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
1417 return extension_items_
.IsCommandIdChecked(id
);
1423 void RenderViewContextMenu::ExecuteCommand(int id
, int event_flags
) {
1424 command_executed_
= true;
1427 // If this command is is added by one of our observers, we dispatch it to the
1429 ObserverListBase
<RenderViewContextMenuObserver
>::Iterator
it(observers_
);
1430 RenderViewContextMenuObserver
* observer
;
1431 while ((observer
= it
.GetNext()) != NULL
) {
1432 if (observer
->IsCommandIdSupported(id
))
1433 return observer
->ExecuteCommand(id
);
1436 RenderFrameHost
* render_frame_host
=
1437 RenderFrameHost::FromID(render_process_id_
, render_frame_id_
);
1439 // Process custom actions range.
1440 if (id
>= IDC_CONTENT_CONTEXT_CUSTOM_FIRST
&&
1441 id
<= IDC_CONTENT_CONTEXT_CUSTOM_LAST
) {
1442 unsigned action
= id
- IDC_CONTENT_CONTEXT_CUSTOM_FIRST
;
1443 const content::CustomContextMenuContext
& context
= params_
.custom_context
;
1444 #if defined(ENABLE_PLUGINS)
1445 if (context
.request_id
&& !context
.is_pepper_menu
) {
1446 ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins(
1447 source_web_contents_
, false, std::string());
1450 source_web_contents_
->ExecuteCustomContextMenuCommand(action
, context
);
1454 // Process extension menu items.
1455 if (id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
1456 id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
1457 extension_items_
.ExecuteCommand(id
, source_web_contents_
, params_
);
1461 if (id
>= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
&&
1462 id
<= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST
) {
1463 ProtocolHandlerRegistry::ProtocolHandlerList handlers
=
1464 GetHandlersForLinkUrl();
1465 if (handlers
.empty()) {
1468 content::RecordAction(
1469 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Open"));
1470 int handlerIndex
= id
- IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST
;
1471 WindowOpenDisposition disposition
=
1472 ForceNewTabDispositionFromEventFlags(event_flags
);
1474 handlers
[handlerIndex
].TranslateUrl(params_
.link_url
),
1475 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
,
1476 disposition
, content::PAGE_TRANSITION_LINK
);
1481 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
: {
1483 chrome::FindBrowserWithWebContents(source_web_contents_
);
1486 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
,
1487 !browser
|| browser
->is_app() ?
1488 NEW_FOREGROUND_TAB
: NEW_BACKGROUND_TAB
,
1489 content::PAGE_TRANSITION_LINK
);
1492 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW
:
1495 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
,
1496 NEW_WINDOW
, content::PAGE_TRANSITION_LINK
);
1499 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD
:
1500 OpenURL(params_
.link_url
, GURL(), OFF_THE_RECORD
,
1501 content::PAGE_TRANSITION_LINK
);
1504 case IDC_CONTENT_CONTEXT_SAVELINKAS
: {
1505 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU
);
1506 const GURL
& referrer
=
1507 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
;
1508 const GURL
& url
= params_
.link_url
;
1509 DownloadManager
* dlm
= BrowserContext::GetDownloadManager(profile_
);
1510 scoped_ptr
<DownloadUrlParameters
> dl_params(
1511 DownloadUrlParameters::FromWebContents(source_web_contents_
, url
));
1512 dl_params
->set_referrer(
1513 content::Referrer(referrer
, params_
.referrer_policy
));
1514 dl_params
->set_referrer_encoding(params_
.frame_charset
);
1515 dl_params
->set_prompt(true);
1516 dlm
->DownloadUrl(dl_params
.Pass());
1520 case IDC_CONTENT_CONTEXT_SAVEAVAS
:
1521 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS
: {
1522 if (params_
.media_type
== WebContextMenuData::MediaTypeCanvas
) {
1523 source_web_contents_
->GetRenderViewHost()->SaveImageAt(
1524 params_
.x
, params_
.y
);
1526 // TODO(zino): We can use SaveImageAt() like a case of canvas.
1527 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU
);
1528 const GURL
& referrer
=
1529 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
;
1530 const GURL
& url
= params_
.src_url
;
1531 source_web_contents_
->SaveFrame(url
, content::Referrer(
1532 referrer
, params_
.referrer_policy
));
1537 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION
:
1538 WriteURLToClipboard(params_
.unfiltered_link_url
);
1541 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION
:
1542 case IDC_CONTENT_CONTEXT_COPYAVLOCATION
:
1543 WriteURLToClipboard(params_
.src_url
);
1546 case IDC_CONTENT_CONTEXT_COPYIMAGE
:
1547 CopyImageAt(params_
.x
, params_
.y
);
1550 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE
:
1551 GetImageThumbnailForSearch();
1554 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB
:
1555 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB
:
1558 params_
.frame_url
.is_empty() ? params_
.page_url
: params_
.frame_url
,
1559 NEW_BACKGROUND_TAB
, content::PAGE_TRANSITION_LINK
);
1562 case IDC_CONTENT_CONTEXT_PLAYPAUSE
: {
1563 bool play
= !!(params_
.media_flags
& WebContextMenuData::MediaPaused
);
1565 content::RecordAction(UserMetricsAction("MediaContextMenu_Play"));
1567 content::RecordAction(UserMetricsAction("MediaContextMenu_Pause"));
1569 MediaPlayerActionAt(gfx::Point(params_
.x
, params_
.y
),
1570 WebMediaPlayerAction(
1571 WebMediaPlayerAction::Play
, play
));
1575 case IDC_CONTENT_CONTEXT_MUTE
: {
1576 bool mute
= !(params_
.media_flags
& WebContextMenuData::MediaMuted
);
1578 content::RecordAction(UserMetricsAction("MediaContextMenu_Mute"));
1580 content::RecordAction(UserMetricsAction("MediaContextMenu_Unmute"));
1582 MediaPlayerActionAt(gfx::Point(params_
.x
, params_
.y
),
1583 WebMediaPlayerAction(
1584 WebMediaPlayerAction::Mute
, mute
));
1588 case IDC_CONTENT_CONTEXT_LOOP
:
1589 content::RecordAction(UserMetricsAction("MediaContextMenu_Loop"));
1590 MediaPlayerActionAt(gfx::Point(params_
.x
, params_
.y
),
1591 WebMediaPlayerAction(
1592 WebMediaPlayerAction::Loop
,
1593 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP
)));
1596 case IDC_CONTENT_CONTEXT_CONTROLS
:
1597 content::RecordAction(UserMetricsAction("MediaContextMenu_Controls"));
1598 MediaPlayerActionAt(
1599 gfx::Point(params_
.x
, params_
.y
),
1600 WebMediaPlayerAction(
1601 WebMediaPlayerAction::Controls
,
1602 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS
)));
1605 case IDC_CONTENT_CONTEXT_ROTATECW
:
1606 content::RecordAction(
1607 UserMetricsAction("PluginContextMenu_RotateClockwise"));
1609 gfx::Point(params_
.x
, params_
.y
),
1611 WebPluginAction::Rotate90Clockwise
,
1615 case IDC_CONTENT_CONTEXT_ROTATECCW
:
1616 content::RecordAction(
1617 UserMetricsAction("PluginContextMenu_RotateCounterclockwise"));
1619 gfx::Point(params_
.x
, params_
.y
),
1621 WebPluginAction::Rotate90Counterclockwise
,
1626 source_web_contents_
->GetController().GoBack();
1630 source_web_contents_
->GetController().GoForward();
1634 source_web_contents_
->OnSavePage();
1638 source_web_contents_
->GetController().Reload(true);
1641 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP
: {
1642 const Extension
* platform_app
= GetExtension();
1643 DCHECK(platform_app
);
1644 DCHECK(platform_app
->is_platform_app());
1646 extensions::ExtensionSystem::Get(profile_
)->extension_service()->
1647 ReloadExtension(platform_app
->id());
1651 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP
: {
1652 const Extension
* platform_app
= GetExtension();
1653 DCHECK(platform_app
);
1654 DCHECK(platform_app
->is_platform_app());
1656 apps::AppLoadService::Get(profile_
)->RestartApplication(
1657 platform_app
->id());
1662 #if defined(ENABLE_PRINTING)
1663 if (params_
.media_type
== WebContextMenuData::MediaTypeNone
) {
1664 #if defined(ENABLE_FULL_PRINTING)
1665 printing::PrintViewManager
* print_view_manager
=
1666 printing::PrintViewManager::FromWebContents(source_web_contents_
);
1668 if (!print_view_manager
)
1670 if (profile_
->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled
)) {
1671 print_view_manager
->PrintNow();
1673 print_view_manager
->PrintPreviewNow(!params_
.selection_text
.empty());
1676 printing::PrintViewManagerBasic
* print_view_manager
=
1677 printing::PrintViewManagerBasic::FromWebContents(
1678 source_web_contents_
);
1679 if (!print_view_manager
)
1681 print_view_manager
->PrintNow();
1682 #endif // defined(ENABLE_FULL_PRINTING)
1684 if (render_frame_host
) {
1685 render_frame_host
->Send(new PrintMsg_PrintNodeUnderContextMenu(
1686 render_frame_host
->GetRoutingID()));
1689 #endif // defined(ENABLE_PRINTING)
1692 case IDC_VIEW_SOURCE
:
1693 source_web_contents_
->ViewSource();
1696 case IDC_CONTENT_CONTEXT_INSPECTELEMENT
:
1697 Inspect(params_
.x
, params_
.y
);
1700 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
: {
1701 const Extension
* platform_app
= GetExtension();
1702 DCHECK(platform_app
);
1703 DCHECK(platform_app
->is_platform_app());
1705 extensions::devtools_util::InspectBackgroundPage(platform_app
, profile_
);
1709 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO
: {
1710 NavigationController
* controller
= &source_web_contents_
->GetController();
1711 // Important to use GetVisibleEntry to match what's showing in the
1712 // omnibox. This may return null.
1713 NavigationEntry
* nav_entry
= controller
->GetVisibleEntry();
1717 chrome::FindBrowserWithWebContents(source_web_contents_
);
1718 chrome::ShowWebsiteSettings(browser
, source_web_contents_
,
1719 nav_entry
->GetURL(), nav_entry
->GetSSL());
1723 case IDC_CONTENT_CONTEXT_TRANSLATE
: {
1724 // A translation might have been triggered by the time the menu got
1725 // selected, do nothing in that case.
1726 TranslateTabHelper
* translate_tab_helper
=
1727 TranslateTabHelper::FromWebContents(source_web_contents_
);
1728 if (!translate_tab_helper
||
1729 translate_tab_helper
->GetLanguageState().IsPageTranslated() ||
1730 translate_tab_helper
->GetLanguageState().translation_pending()) {
1733 std::string original_lang
=
1734 translate_tab_helper
->GetLanguageState().original_language();
1735 std::string target_lang
= g_browser_process
->GetApplicationLocale();
1736 target_lang
= TranslateDownloadManager::GetLanguageCode(target_lang
);
1737 // Since the user decided to translate for that language and site, clears
1738 // any preferences for not translating them.
1739 scoped_ptr
<TranslatePrefs
> prefs(
1740 TranslateTabHelper::CreateTranslatePrefs(profile_
->GetPrefs()));
1741 prefs
->UnblockLanguage(original_lang
);
1742 prefs
->RemoveSiteFromBlacklist(params_
.page_url
.HostNoBrackets());
1743 TranslateManager
* manager
= translate_tab_helper
->GetTranslateManager();
1745 manager
->TranslatePage(original_lang
, target_lang
, true);
1749 case IDC_CONTENT_CONTEXT_RELOADFRAME
:
1750 // We always obey the cache here.
1751 // TODO(evanm): Perhaps we could allow shift-clicking the menu item to do
1752 // a cache-ignoring reload of the frame.
1753 source_web_contents_
->ReloadFocusedFrame(false);
1756 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE
:
1757 source_web_contents_
->ViewFrameSource(params_
.frame_url
,
1758 params_
.frame_page_state
);
1761 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO
: {
1762 Browser
* browser
= chrome::FindBrowserWithWebContents(
1763 source_web_contents_
);
1764 chrome::ShowWebsiteSettings(browser
, source_web_contents_
,
1765 params_
.frame_url
, params_
.security_info
);
1769 case IDC_CONTENT_CONTEXT_UNDO
:
1770 source_web_contents_
->Undo();
1773 case IDC_CONTENT_CONTEXT_REDO
:
1774 source_web_contents_
->Redo();
1777 case IDC_CONTENT_CONTEXT_CUT
:
1778 source_web_contents_
->Cut();
1781 case IDC_CONTENT_CONTEXT_COPY
:
1782 source_web_contents_
->Copy();
1785 case IDC_CONTENT_CONTEXT_PASTE
:
1786 source_web_contents_
->Paste();
1789 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE
:
1790 source_web_contents_
->PasteAndMatchStyle();
1793 case IDC_CONTENT_CONTEXT_DELETE
:
1794 source_web_contents_
->Delete();
1797 case IDC_CONTENT_CONTEXT_SELECTALL
:
1798 source_web_contents_
->SelectAll();
1801 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR
:
1802 case IDC_CONTENT_CONTEXT_GOTOURL
: {
1803 WindowOpenDisposition disposition
=
1804 ForceNewTabDispositionFromEventFlags(event_flags
);
1805 OpenURL(selection_navigation_url_
, GURL(), disposition
,
1806 content::PAGE_TRANSITION_LINK
);
1809 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS
: {
1810 WindowOpenDisposition disposition
=
1811 ForceNewTabDispositionFromEventFlags(event_flags
);
1812 GURL url
= chrome::GetSettingsUrl(chrome::kLanguageOptionsSubPage
);
1813 OpenURL(url
, GURL(), disposition
, content::PAGE_TRANSITION_LINK
);
1817 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS
: {
1818 content::RecordAction(
1819 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Settings"));
1820 WindowOpenDisposition disposition
=
1821 ForceNewTabDispositionFromEventFlags(event_flags
);
1822 GURL url
= chrome::GetSettingsUrl(chrome::kHandlerSettingsSubPage
);
1823 OpenURL(url
, GURL(), disposition
, content::PAGE_TRANSITION_LINK
);
1827 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE
: {
1828 // Make sure the model is loaded.
1829 TemplateURLService
* model
=
1830 TemplateURLServiceFactory::GetForProfile(profile_
);
1835 SearchEngineTabHelper
* search_engine_tab_helper
=
1836 SearchEngineTabHelper::FromWebContents(source_web_contents_
);
1837 if (search_engine_tab_helper
&&
1838 search_engine_tab_helper
->delegate()) {
1839 base::string16
keyword(
1840 TemplateURLService::GenerateKeyword(params_
.page_url
));
1841 TemplateURLData data
;
1842 data
.short_name
= keyword
;
1843 data
.SetKeyword(keyword
);
1844 data
.SetURL(params_
.keyword_url
.spec());
1846 TemplateURL::GenerateFaviconURL(params_
.page_url
.GetOrigin());
1847 // Takes ownership of the TemplateURL.
1848 search_engine_tab_helper
->delegate()->
1849 ConfirmAddSearchProvider(new TemplateURL(profile_
, data
), profile_
);
1860 ProtocolHandlerRegistry::ProtocolHandlerList
1861 RenderViewContextMenu::GetHandlersForLinkUrl() {
1862 ProtocolHandlerRegistry::ProtocolHandlerList handlers
=
1863 protocol_handler_registry_
->GetHandlersFor(params_
.link_url
.scheme());
1864 std::sort(handlers
.begin(), handlers
.end());
1868 void RenderViewContextMenu::MenuWillShow(ui::SimpleMenuModel
* source
) {
1869 for (int i
= 0; i
< source
->GetItemCount(); ++i
) {
1870 if (source
->IsVisibleAt(i
) &&
1871 source
->GetTypeAt(i
) != ui::MenuModel::TYPE_SEPARATOR
) {
1872 RecordShownItem(source
->GetCommandIdAt(i
));
1876 // Ignore notifications from submenus.
1877 if (source
!= &menu_model_
)
1880 content::RenderWidgetHostView
* view
=
1881 source_web_contents_
->GetRenderWidgetHostView();
1883 view
->SetShowingContextMenu(true);
1885 content::NotificationService::current()->Notify(
1886 chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN
,
1887 content::Source
<RenderViewContextMenu
>(this),
1888 content::NotificationService::NoDetails());
1891 void RenderViewContextMenu::MenuClosed(ui::SimpleMenuModel
* source
) {
1892 // Ignore notifications from submenus.
1893 if (source
!= &menu_model_
)
1896 content::RenderWidgetHostView
* view
=
1897 source_web_contents_
->GetRenderWidgetHostView();
1899 view
->SetShowingContextMenu(false);
1900 source_web_contents_
->NotifyContextMenuClosed(params_
.custom_context
);
1902 if (!command_executed_
) {
1903 FOR_EACH_OBSERVER(RenderViewContextMenuObserver
,
1909 bool RenderViewContextMenu::IsDevCommandEnabled(int id
) const {
1910 if (id
== IDC_CONTENT_CONTEXT_INSPECTELEMENT
||
1911 id
== IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE
) {
1912 const CommandLine
* command_line
= CommandLine::ForCurrentProcess();
1913 if (!profile_
->GetPrefs()->GetBoolean(prefs::kWebKitJavascriptEnabled
) ||
1914 command_line
->HasSwitch(switches::kDisableJavaScript
))
1917 // Don't enable the web inspector if the developer tools are disabled via
1918 // the preference dev-tools-disabled.
1919 if (profile_
->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled
))
1926 base::string16
RenderViewContextMenu::PrintableSelectionText() {
1927 return gfx::TruncateString(params_
.selection_text
,
1928 kMaxSelectionTextLength
);
1931 // Controller functions --------------------------------------------------------
1933 void RenderViewContextMenu::OpenURL(
1934 const GURL
& url
, const GURL
& referring_url
,
1935 WindowOpenDisposition disposition
,
1936 content::PageTransition transition
) {
1937 content::Referrer
referrer(referring_url
.GetAsReferrer(),
1938 params_
.referrer_policy
);
1940 WebContents
* new_contents
= source_web_contents_
->OpenURL(OpenURLParams(
1941 url
, referrer
, disposition
, transition
, false));
1945 RetargetingDetails details
;
1946 details
.source_web_contents
= source_web_contents_
;
1947 details
.source_render_frame_id
= render_frame_id_
;
1948 details
.target_url
= url
;
1949 details
.target_web_contents
= new_contents
;
1950 details
.not_yet_in_tabstrip
= false;
1951 content::NotificationService::current()->Notify(
1952 chrome::NOTIFICATION_RETARGETING
,
1953 content::Source
<Profile
>(Profile::FromBrowserContext(
1954 source_web_contents_
->GetBrowserContext())),
1955 content::Details
<RetargetingDetails
>(&details
));
1958 void RenderViewContextMenu::CopyImageAt(int x
, int y
) {
1959 source_web_contents_
->GetRenderViewHost()->CopyImageAt(x
, y
);
1962 void RenderViewContextMenu::GetImageThumbnailForSearch() {
1963 RenderFrameHost
* render_frame_host
=
1964 RenderFrameHost::FromID(render_process_id_
, render_frame_id_
);
1965 if (!render_frame_host
)
1967 render_frame_host
->Send(new ChromeViewMsg_RequestThumbnailForContextNode(
1968 render_frame_host
->GetRoutingID(),
1969 kImageSearchThumbnailMinSize
,
1970 gfx::Size(kImageSearchThumbnailMaxWidth
,
1971 kImageSearchThumbnailMaxHeight
)));
1974 void RenderViewContextMenu::Inspect(int x
, int y
) {
1975 content::RecordAction(UserMetricsAction("DevTools_InspectElement"));
1976 RenderFrameHost
* render_frame_host
=
1977 RenderFrameHost::FromID(render_process_id_
, render_frame_id_
);
1978 if (!render_frame_host
)
1980 DevToolsWindow::InspectElement(render_frame_host
->GetRenderViewHost(), x
, y
);
1983 void RenderViewContextMenu::WriteURLToClipboard(const GURL
& url
) {
1984 chrome_common_net::WriteURLToClipboard(
1986 profile_
->GetPrefs()->GetString(prefs::kAcceptLanguages
),
1987 ui::Clipboard::GetForCurrentThread());
1990 void RenderViewContextMenu::MediaPlayerActionAt(
1991 const gfx::Point
& location
,
1992 const WebMediaPlayerAction
& action
) {
1993 source_web_contents_
->GetRenderViewHost()->
1994 ExecuteMediaPlayerActionAtLocation(location
, action
);
1997 void RenderViewContextMenu::PluginActionAt(
1998 const gfx::Point
& location
,
1999 const WebPluginAction
& action
) {
2000 source_web_contents_
->GetRenderViewHost()->
2001 ExecutePluginActionAtLocation(location
, action
);