Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / tab_contents / render_view_context_menu.cc
blob98787bf86307a7c7dfb643feccbbafa14f48f1d7
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"
7 #include <algorithm>
8 #include <set>
9 #include <utility>
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"
105 #else
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;
115 using blink::WebURL;
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;
130 namespace {
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 {
142 int enum_id;
143 int control_id;
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 },
166 { 21, IDC_BACK },
167 { 22, IDC_FORWARD },
168 { 23, IDC_SAVE_PAGE },
169 { 24, IDC_RELOAD },
170 { 25, IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP },
171 { 26, IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP },
172 { 27, IDC_PRINT },
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;
229 return id;
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;
241 return -1;
244 // Increments histogram value for used items specified by |id|.
245 void RecordUsedItem(int id) {
246 int enum_id = FindUMAEnumValueForCommand(id);
247 if (enum_id != -1) {
248 const size_t kMappingSize = arraysize(kUmaEnumToControlId);
249 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Used", enum_id,
250 kUmaEnumToControlId[kMappingSize - 1].enum_id);
251 } else {
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);
259 if (enum_id != -1) {
260 const size_t kMappingSize = arraysize(kUmaEnumToControlId);
261 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Shown", enum_id,
262 kUmaEnumToControlId[kMappingSize - 1].enum_id);
263 } else {
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(
274 int event_flags) {
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;
285 if (action_id == id)
286 return items[i].enabled;
287 if (items[i].type == content::MenuItem::SUBMENU) {
288 if (IsCustomItemEnabled(items[i].submenu, id))
289 return true;
292 return false;
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;
300 if (action_id == id)
301 return items[i].checked;
302 if (items[i].type == content::MenuItem::SUBMENU) {
303 if (IsCustomItemChecked(items[i].submenu, id))
304 return true;
307 return false;
310 const size_t kMaxCustomMenuDepth = 5;
311 const size_t kMaxCustomMenuTotalItems = 1000;
313 void AddCustomItemsToMenu(const std::vector<content::MenuItem>& items,
314 size_t depth,
315 size_t* total_items,
316 ui::SimpleMenuModel::Delegate* delegate,
317 ui::SimpleMenuModel* menu_model) {
318 if (depth > kMaxCustomMenuDepth) {
319 LOG(ERROR) << "Custom menu too deeply nested.";
320 return;
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.";
326 return;
328 if (*total_items >= kMaxCustomMenuTotalItems) {
329 LOG(ERROR) << "Custom menu too large (too many items).";
330 return;
332 (*total_items)++;
333 switch (items[i].type) {
334 case content::MenuItem::OPTION:
335 menu_model->AddItem(
336 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
337 items[i].label);
338 break;
339 case content::MenuItem::CHECKABLE_OPTION:
340 menu_model->AddCheckItem(
341 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
342 items[i].label);
343 break;
344 case content::MenuItem::GROUP:
345 // TODO(viettrungluu): I don't know what this is supposed to do.
346 NOTREACHED();
347 break;
348 case content::MenuItem::SEPARATOR:
349 menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
350 break;
351 case content::MenuItem::SUBMENU: {
352 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate);
353 AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate,
354 submenu);
355 menu_model->AddSubMenu(
356 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
357 items[i].label,
358 submenu);
359 break;
361 default:
362 NOTREACHED();
363 break;
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);
378 } // namespace
380 // static
381 const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50;
383 // static
384 bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) {
385 return url.SchemeIs(chrome::kChromeDevToolsScheme);
388 // static
389 bool RenderViewContextMenu::IsInternalResourcesURL(const GURL& url) {
390 if (!url.SchemeIs(chrome::kChromeUIScheme))
391 return false;
392 return url.host() == chrome::kChromeUISyncResourcesHost;
395 static const int kSpellcheckRadioGroup = 1;
397 RenderViewContextMenu::RenderViewContextMenu(
398 WebContents* web_contents,
399 const content::ContextMenuParams& params)
400 : params_(params),
401 source_web_contents_(web_contents),
402 profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
403 menu_model_(this),
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),
411 is_guest_(false) {
412 RenderViewHost* rvh = source_web_contents_->GetRenderViewHost();
413 if (rvh && rvh->GetProcess()->IsGuest())
414 is_guest_ = true;
417 RenderViewContextMenu::~RenderViewContextMenu() {
420 // Menu construction functions -------------------------------------------------
422 void RenderViewContextMenu::Init() {
423 InitMenu();
424 PlatformInit();
427 void RenderViewContextMenu::Cancel() {
428 PlatformCancel();
431 static bool ExtensionPatternMatch(const extensions::URLPatternSet& patterns,
432 const GURL& url) {
433 // No patterns means no restriction, so that implicitly matches.
434 if (patterns.is_empty())
435 return true;
436 return patterns.MatchesURL(url);
439 // static
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)))
452 return true;
454 if (has_link && contexts.Contains(MenuItem::LINK) &&
455 ExtensionPatternMatch(target_url_patterns, params.link_url))
456 return true;
458 switch (params.media_type) {
459 case WebContextMenuData::MediaTypeImage:
460 if (contexts.Contains(MenuItem::IMAGE) &&
461 ExtensionPatternMatch(target_url_patterns, params.src_url))
462 return true;
463 break;
465 case WebContextMenuData::MediaTypeVideo:
466 if (contexts.Contains(MenuItem::VIDEO) &&
467 ExtensionPatternMatch(target_url_patterns, params.src_url))
468 return true;
469 break;
471 case WebContextMenuData::MediaTypeAudio:
472 if (contexts.Contains(MenuItem::AUDIO) &&
473 ExtensionPatternMatch(target_url_patterns, params.src_url))
474 return true;
475 break;
477 default:
478 break;
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))
487 return true;
489 return false;
492 static const GURL& GetDocumentURL(const content::ContextMenuParams& params) {
493 return params.frame_url.is_empty() ? params.page_url : params.frame_url;
496 // static
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());
502 if (!match)
503 return false;
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();
513 if (!service)
514 return; // In unit-tests, we may not have an ExtensionService.
516 MenuManager* menu_manager = MenuManager::Get(profile_);
517 if (!menu_manager)
518 return;
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())
540 return;
542 const std::string app_locale = g_browser_process->GetApplicationLocale();
543 l10n_util::SortStrings16(app_locale, &sorted_menu_titles);
545 int index = 0;
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,
550 &index);
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();
561 return;
564 extensions::ViewType view_type =
565 extensions::GetViewType(source_web_contents_);
566 if (view_type == extensions::VIEW_TYPE_APP_SHELL) {
567 AppendPlatformAppItems();
568 return;
569 } else if (view_type == extensions::VIEW_TYPE_EXTENSION_POPUP) {
570 AppendPopupExtensionItems();
571 return;
572 } else if (view_type == extensions::VIEW_TYPE_PANEL) {
573 AppendPanelItems();
574 return;
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.
583 if (has_selection) {
584 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
585 } else {
586 // Don't add items for Pepper menu.
587 if (!params_.custom_context.is_pepper_menu)
588 AppendDeveloperItems();
589 return;
593 // When no special node or text is selected and selection has no link,
594 // show page items.
595 if (params_.media_type == WebContextMenuData::MediaTypeNone &&
596 !has_link &&
597 !params_.is_editable &&
598 !is_guest_ &&
599 !has_selection) {
600 if (!params_.page_url.is_empty()) {
601 bool is_devtools = IsDevToolsURL(params_.page_url);
602 if (!is_devtools && !IsInternalResourcesURL(params_.page_url)) {
603 AppendPageItems();
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);
609 AppendFrameItems();
613 } else {
614 DCHECK(params_.frame_url.is_empty());
618 // Do not show link related items for guest.
619 if (has_link && !is_guest_) {
620 AppendLinkItems();
621 if (params_.media_type != WebContextMenuData::MediaTypeNone)
622 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
625 switch (params_.media_type) {
626 case WebContextMenuData::MediaTypeNone:
627 break;
628 case WebContextMenuData::MediaTypeImage:
629 AppendImageItems();
630 break;
631 case WebContextMenuData::MediaTypeVideo:
632 AppendVideoItems();
633 break;
634 case WebContextMenuData::MediaTypeAudio:
635 AppendAudioItems();
636 break;
637 case WebContextMenuData::MediaTypePlugin:
638 AppendPluginItems();
639 break;
640 #ifdef WEBCONTEXT_MEDIATYPEFILE_DEFINED
641 case WebContextMenuData::MediaTypeFile:
642 break;
643 #endif
646 if (params_.is_editable)
647 AppendEditableItems();
648 else if (has_selection)
649 AppendCopyItem();
651 if (!is_guest_ && has_selection) {
652 AppendSearchProvider();
653 if (!IsDevToolsURL(params_.page_url))
654 AppendPrintItem();
657 if (!IsDevToolsURL(params_.page_url) && !is_guest_)
658 AppendAllExtensionItems();
660 AppendDeveloperItems();
662 if (!is_guest_) {
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());
670 #endif
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())
679 return NULL;
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)
691 AppendCopyItem();
694 void RenderViewContextMenu::AppendPlatformAppItems() {
695 const Extension* platform_app = GetExtension();
697 // The RVH might be for a process sandboxed from the extension.
698 if (!platform_app)
699 return;
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)
709 AppendCopyItem();
711 int index = 0;
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)
738 AppendCopyItem();
740 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())
753 AppendLinkItems();
755 if (params_.is_editable)
756 AppendEditableItems();
757 else if (has_selection)
758 AppendCopyItem();
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
762 // package.
763 const Extension* extension = GetExtension();
764 if (extension) {
765 // Only add extension items from this extension.
766 int index = 0;
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,
793 bool enabled,
794 bool hidden,
795 const base::string16& label) {
796 // This function needs platform-specific implementation.
797 NOTIMPLEMENTED();
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 {
809 return profile_;
812 bool RenderViewContextMenu::AppendCustomItems() {
813 size_t total_items = 0;
814 AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this,
815 &menu_model_);
816 return total_items > 0;
819 void RenderViewContextMenu::AppendDeveloperItems() {
820 // Show Inspect Element in DevTools itself only in case of the debug
821 // devtools build.
822 bool show_developer_items = !IsDevToolsURL(params_.page_url);
824 #if defined(DEBUG_DEVTOOLS)
825 show_developer_items = true;
826 #endif
828 if (!show_developer_items)
829 return;
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()) {
876 menu_model_.AddItem(
877 IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
878 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
879 default_provider->short_name()));
881 AppendPrintItem();
884 void RenderViewContextMenu::AppendAudioItems() {
885 AppendMediaItems();
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() {
896 AppendMediaItems();
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())
931 AppendPageItems();
932 } else {
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);
961 menu_model_.AddItem(
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() {
999 DCHECK(profile_);
1001 TrimWhitespace(params_.selection_text, TRIM_ALL, &params_.selection_text);
1002 if (params_.selection_text.empty())
1003 return;
1005 base::ReplaceChars(params_.selection_text, AutocompleteMatch::kInvalidChars,
1006 base::ASCIIToUTF16(" "), &params_.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())
1013 return;
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)
1023 return;
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));
1029 } else {
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())
1118 return;
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
1144 // observer.
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))
1158 return false;
1160 if (id == IDC_SAVE_PAGE &&
1161 (content_restrictions & CONTENT_RESTRICTION_SAVE)) {
1162 return false;
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);
1171 // Custom items.
1172 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1173 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1174 return IsCustomItemEnabled(params_.custom_items, id);
1177 // Extension items.
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) {
1185 return true;
1188 IncognitoModePrefs::Availability incognito_avail =
1189 IncognitoModePrefs::GetAvailability(profile_->GetPrefs());
1190 switch (id) {
1191 case IDC_BACK:
1192 return source_web_contents_->GetController().CanGoBack();
1194 case IDC_FORWARD:
1195 return source_web_contents_->GetController().CanGoForward();
1197 case IDC_RELOAD: {
1198 CoreTabHelper* core_tab_helper =
1199 CoreTabHelper::FromWebContents(source_web_contents_);
1200 if (!core_tab_helper)
1201 return false;
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)
1220 return false;
1221 // Disabled if no browser is associated (e.g. desktop notifications).
1222 if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL)
1223 return false;
1224 return true;
1226 case IDC_CONTENT_CONTEXT_TRANSLATE: {
1227 TranslateTabHelper* translate_tab_helper =
1228 TranslateTabHelper::FromWebContents(source_web_contents_);
1229 if (!translate_tab_helper)
1230 return false;
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
1238 // language.
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))
1262 return false;
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))
1273 return false;
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
1290 // error state.
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
1305 // return it back.
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:
1312 return
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))
1324 return false;
1326 const GURL& url = params_.src_url;
1327 bool can_save =
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));
1334 #endif
1335 return can_save;
1338 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
1339 return true;
1341 case IDC_SAVE_PAGE: {
1342 CoreTabHelper* core_tab_helper =
1343 CoreTabHelper::FromWebContents(source_web_contents_);
1344 if (!core_tab_helper)
1345 return false;
1347 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
1348 if (core_delegate &&
1349 !core_delegate->CanSaveContents(source_web_contents_))
1350 return false;
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))
1356 return false;
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;
1398 case IDC_PRINT:
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:
1407 return true;
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)
1411 return false;
1412 return true;
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:
1421 return true;
1422 #endif
1424 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE:
1425 return !params_.keyword_url.is_empty();
1427 case IDC_SPELLCHECK_MENU:
1428 return true;
1430 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES:
1431 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT:
1432 case IDC_SPEECH_INPUT_MENU:
1433 return true;
1435 case IDC_CONTENT_CONTEXT_OPENLINKWITH:
1436 return true;
1438 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS:
1439 return true;
1441 default:
1442 NOTREACHED();
1443 return false;
1447 bool RenderViewContextMenu::IsCommandIdChecked(int id) const {
1448 // If this command is is added by one of our observers, we dispatch it to the
1449 // observer.
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;
1468 // Custom items.
1469 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1470 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1471 return IsCustomItemChecked(params_.custom_items, id);
1474 // Extension items.
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);
1486 #endif
1488 return false;
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
1494 // observer.
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);
1502 RecordUsedItem(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());
1516 #endif
1517 rvh->ExecuteCustomContextMenuCommand(action, context);
1518 return;
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_);
1525 return;
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()) {
1533 return;
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);
1540 OpenURL(
1541 handlers[handlerIndex].TranslateUrl(params_.link_url),
1542 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1543 params_.frame_id,
1544 disposition,
1545 content::PAGE_TRANSITION_LINK);
1546 return;
1549 switch (id) {
1550 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: {
1551 Browser* browser =
1552 chrome::FindBrowserWithWebContents(source_web_contents_);
1553 OpenURL(
1554 params_.link_url,
1555 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1556 params_.frame_id,
1557 !browser || browser->is_app() ?
1558 NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB,
1559 content::PAGE_TRANSITION_LINK);
1560 break;
1562 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
1563 OpenURL(
1564 params_.link_url,
1565 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1566 params_.frame_id,
1567 NEW_WINDOW, content::PAGE_TRANSITION_LINK);
1568 break;
1570 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
1571 OpenURL(params_.link_url,
1572 GURL(),
1573 params_.frame_id,
1574 OFF_THE_RECORD,
1575 content::PAGE_TRANSITION_LINK);
1576 break;
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());
1591 break;
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;
1600 int64 post_id = -1;
1601 if (url == source_web_contents_->GetURL()) {
1602 const NavigationEntry* entry =
1603 source_web_contents_->GetController().GetActiveEntry();
1604 if (entry)
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);
1614 if (post_id >= 0)
1615 dl_params->set_method("POST");
1616 dl_params->set_prompt(true);
1617 dlm->DownloadUrl(dl_params.Pass());
1618 break;
1621 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION:
1622 WriteURLToClipboard(params_.unfiltered_link_url);
1623 break;
1625 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION:
1626 case IDC_CONTENT_CONTEXT_COPYAVLOCATION:
1627 WriteURLToClipboard(params_.src_url);
1628 break;
1630 case IDC_CONTENT_CONTEXT_COPYIMAGE:
1631 CopyImageAt(params_.x, params_.y);
1632 break;
1634 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
1635 GetImageThumbnailForSearch();
1636 break;
1638 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
1639 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
1640 OpenURL(
1641 params_.src_url,
1642 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1643 params_.frame_id,
1644 NEW_BACKGROUND_TAB, content::PAGE_TRANSITION_LINK);
1645 break;
1647 case IDC_CONTENT_CONTEXT_PLAYPAUSE: {
1648 bool play = !!(params_.media_flags & WebContextMenuData::MediaPaused);
1649 if (play) {
1650 content::RecordAction(UserMetricsAction("MediaContextMenu_Play"));
1651 } else {
1652 content::RecordAction(UserMetricsAction("MediaContextMenu_Pause"));
1654 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1655 WebMediaPlayerAction(
1656 WebMediaPlayerAction::Play, play));
1657 break;
1660 case IDC_CONTENT_CONTEXT_MUTE: {
1661 bool mute = !(params_.media_flags & WebContextMenuData::MediaMuted);
1662 if (mute) {
1663 content::RecordAction(UserMetricsAction("MediaContextMenu_Mute"));
1664 } else {
1665 content::RecordAction(UserMetricsAction("MediaContextMenu_Unmute"));
1667 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1668 WebMediaPlayerAction(
1669 WebMediaPlayerAction::Mute, mute));
1670 break;
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)));
1679 break;
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)));
1688 break;
1690 case IDC_CONTENT_CONTEXT_ROTATECW:
1691 content::RecordAction(
1692 UserMetricsAction("PluginContextMenu_RotateClockwise"));
1693 PluginActionAt(
1694 gfx::Point(params_.x, params_.y),
1695 WebPluginAction(
1696 WebPluginAction::Rotate90Clockwise,
1697 true));
1698 break;
1700 case IDC_CONTENT_CONTEXT_ROTATECCW:
1701 content::RecordAction(
1702 UserMetricsAction("PluginContextMenu_RotateCounterclockwise"));
1703 PluginActionAt(
1704 gfx::Point(params_.x, params_.y),
1705 WebPluginAction(
1706 WebPluginAction::Rotate90Counterclockwise,
1707 true));
1708 break;
1710 case IDC_BACK:
1711 source_web_contents_->GetController().GoBack();
1712 break;
1714 case IDC_FORWARD:
1715 source_web_contents_->GetController().GoForward();
1716 break;
1718 case IDC_SAVE_PAGE:
1719 source_web_contents_->OnSavePage();
1720 break;
1722 case IDC_RELOAD:
1723 source_web_contents_->GetController().Reload(true);
1724 break;
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());
1733 break;
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());
1743 break;
1746 case IDC_PRINT:
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)
1754 break;
1755 if (profile_->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled)) {
1756 print_view_manager->PrintNow();
1757 } else {
1758 print_view_manager->PrintPreviewNow(!params_.selection_text.empty());
1760 #else
1761 printing::PrintViewManagerBasic* print_view_manager =
1762 printing::PrintViewManagerBasic::FromWebContents(
1763 source_web_contents_);
1764 if (!print_view_manager)
1765 break;
1766 print_view_manager->PrintNow();
1767 #endif // defined(ENABLE_FULL_PRINTING)
1768 } else {
1769 rvh->Send(new PrintMsg_PrintNodeUnderContextMenu(rvh->GetRoutingID()));
1771 #endif // defined(ENABLE_PRINTING)
1772 break;
1774 case IDC_VIEW_SOURCE:
1775 source_web_contents_->ViewSource();
1776 break;
1778 case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
1779 Inspect(params_.x, params_.y);
1780 break;
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_);
1788 break;
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
1794 // omnibox.
1795 NavigationEntry* nav_entry = controller->GetVisibleEntry();
1796 Browser* browser =
1797 chrome::FindBrowserWithWebContents(source_web_contents_);
1798 chrome::ShowWebsiteSettings(browser, source_web_contents_,
1799 nav_entry->GetURL(), nav_entry->GetSSL());
1800 break;
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()) {
1811 return;
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);
1824 break;
1827 case IDC_CONTENT_CONTEXT_RELOADFRAME:
1828 rvh->ReloadFrame();
1829 break;
1831 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
1832 source_web_contents_->ViewFrameSource(params_.frame_url,
1833 params_.frame_page_state);
1834 break;
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);
1841 break;
1844 case IDC_CONTENT_CONTEXT_UNDO:
1845 rvh->Undo();
1846 break;
1848 case IDC_CONTENT_CONTEXT_REDO:
1849 rvh->Redo();
1850 break;
1852 case IDC_CONTENT_CONTEXT_CUT:
1853 rvh->Cut();
1854 break;
1856 case IDC_CONTENT_CONTEXT_COPY:
1857 rvh->Copy();
1858 break;
1860 case IDC_CONTENT_CONTEXT_PASTE:
1861 rvh->Paste();
1862 break;
1864 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE:
1865 rvh->PasteAndMatchStyle();
1866 break;
1868 case IDC_CONTENT_CONTEXT_DELETE:
1869 rvh->Delete();
1870 break;
1872 case IDC_CONTENT_CONTEXT_SELECTALL:
1873 rvh->SelectAll();
1874 break;
1876 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR:
1877 case IDC_CONTENT_CONTEXT_GOTOURL: {
1878 WindowOpenDisposition disposition =
1879 ForceNewTabDispositionFromEventFlags(event_flags);
1880 OpenURL(selection_navigation_url_,
1881 GURL(),
1882 params_.frame_id,
1883 disposition,
1884 content::PAGE_TRANSITION_LINK);
1885 break;
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);
1893 break;
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);
1904 break;
1907 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE: {
1908 // Make sure the model is loaded.
1909 TemplateURLService* model =
1910 TemplateURLServiceFactory::GetForProfile(profile_);
1911 if (!model)
1912 return;
1913 model->Load();
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());
1925 data.favicon_url =
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_);
1931 break;
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));
1940 break;
1942 #endif
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);
1949 break;
1952 default:
1953 NOTREACHED();
1954 break;
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());
1963 return handlers;
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_)
1976 return;
1978 content::RenderWidgetHostView* view =
1979 source_web_contents_->GetRenderWidgetHostView();
1980 if (view)
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_)
1992 return;
1994 content::RenderWidgetHostView* view =
1995 source_web_contents_->GetRenderWidgetHostView();
1996 if (view)
1997 view->SetShowingContextMenu(false);
1998 RenderViewHost* rvh = source_web_contents_->GetRenderViewHost();
1999 if (rvh) {
2000 rvh->NotifyContextMenuClosed(params_.custom_context);
2003 if (!command_executed_) {
2004 FOR_EACH_OBSERVER(RenderViewContextMenuObserver,
2005 observers_,
2006 OnMenuCancel());
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))
2016 return false;
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))
2021 return false;
2024 return true;
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
2039 // in the referrer.
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));
2053 if (!new_contents)
2054 return;
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(
2090 url,
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);