NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / tab_contents / render_view_context_menu.cc
blob58edbb83e0b5c805199792cc6b2ffab369293538
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_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_host.h"
37 #include "chrome/browser/extensions/extension_service.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_tab_helper.h"
55 #include "chrome/browser/ui/browser.h"
56 #include "chrome/browser/ui/browser_commands.h"
57 #include "chrome/browser/ui/browser_finder.h"
58 #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h"
59 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
60 #include "chrome/common/chrome_constants.h"
61 #include "chrome/common/chrome_switches.h"
62 #include "chrome/common/content_restriction.h"
63 #include "chrome/common/net/url_util.h"
64 #include "chrome/common/pref_names.h"
65 #include "chrome/common/render_messages.h"
66 #include "chrome/common/spellcheck_messages.h"
67 #include "chrome/common/url_constants.h"
68 #include "components/translate/core/browser/translate_download_manager.h"
69 #include "components/translate/core/browser/translate_prefs.h"
70 #include "content/public/browser/child_process_security_policy.h"
71 #include "content/public/browser/download_manager.h"
72 #include "content/public/browser/download_save_info.h"
73 #include "content/public/browser/download_url_parameters.h"
74 #include "content/public/browser/navigation_details.h"
75 #include "content/public/browser/navigation_entry.h"
76 #include "content/public/browser/notification_service.h"
77 #include "content/public/browser/render_frame_host.h"
78 #include "content/public/browser/render_process_host.h"
79 #include "content/public/browser/render_view_host.h"
80 #include "content/public/browser/render_widget_host_view.h"
81 #include "content/public/browser/user_metrics.h"
82 #include "content/public/browser/web_contents.h"
83 #include "content/public/common/menu_item.h"
84 #include "content/public/common/ssl_status.h"
85 #include "content/public/common/url_utils.h"
86 #include "extensions/browser/extension_system.h"
87 #include "extensions/browser/view_type_utils.h"
88 #include "extensions/common/extension.h"
89 #include "grit/generated_resources.h"
90 #include "net/base/escape.h"
91 #include "third_party/WebKit/public/web/WebContextMenuData.h"
92 #include "third_party/WebKit/public/web/WebMediaPlayerAction.h"
93 #include "third_party/WebKit/public/web/WebPluginAction.h"
94 #include "ui/base/clipboard/clipboard.h"
95 #include "ui/base/l10n/l10n_util.h"
96 #include "ui/gfx/favicon_size.h"
97 #include "ui/gfx/point.h"
98 #include "ui/gfx/size.h"
99 #include "ui/gfx/text_elider.h"
101 #if defined(ENABLE_PRINTING)
102 #include "chrome/common/print_messages.h"
104 #if defined(ENABLE_FULL_PRINTING)
105 #include "chrome/browser/printing/print_preview_context_menu_observer.h"
106 #include "chrome/browser/printing/print_preview_dialog_controller.h"
107 #include "chrome/browser/printing/print_view_manager.h"
108 #else
109 #include "chrome/browser/printing/print_view_manager_basic.h"
110 #endif // defined(ENABLE_FULL_PRINTING)
111 #endif // defined(ENABLE_PRINTING)
113 using base::UserMetricsAction;
114 using blink::WebContextMenuData;
115 using blink::WebMediaPlayerAction;
116 using blink::WebPluginAction;
117 using blink::WebString;
118 using blink::WebURL;
119 using content::BrowserContext;
120 using content::ChildProcessSecurityPolicy;
121 using content::DownloadManager;
122 using content::DownloadUrlParameters;
123 using content::NavigationController;
124 using content::NavigationEntry;
125 using content::OpenURLParams;
126 using content::RenderFrameHost;
127 using content::RenderViewHost;
128 using content::SSLStatus;
129 using content::WebContents;
130 using extensions::Extension;
131 using extensions::MenuItem;
132 using extensions::MenuManager;
134 namespace {
136 const int kImageSearchThumbnailMinSize = 300 * 300;
137 const int kImageSearchThumbnailMaxWidth = 600;
138 const int kImageSearchThumbnailMaxHeight = 600;
140 // Maps UMA enumeration to IDC. IDC could be changed so we can't use
141 // just them and |UMA_HISTOGRAM_CUSTOM_ENUMERATION|.
142 // Never change mapping or reuse |enum_id|. Always push back new items.
143 // Items that is not used any more by |RenderViewContextMenu.ExecuteCommand|
144 // could be deleted, but don't change the rest of |kUmaEnumToControlId|.
145 const struct UmaEnumCommandIdPair {
146 int enum_id;
147 int control_id;
148 } kUmaEnumToControlId[] = {
149 { 0, IDC_CONTENT_CONTEXT_CUSTOM_FIRST },
150 { 1, IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST },
151 { 2, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST },
152 { 3, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB },
153 { 4, IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW },
154 { 5, IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD },
155 { 6, IDC_CONTENT_CONTEXT_SAVELINKAS },
156 { 7, IDC_CONTENT_CONTEXT_SAVEAVAS },
157 { 8, IDC_CONTENT_CONTEXT_SAVEIMAGEAS },
158 { 9, IDC_CONTENT_CONTEXT_COPYLINKLOCATION },
159 { 10, IDC_CONTENT_CONTEXT_COPYIMAGELOCATION },
160 { 11, IDC_CONTENT_CONTEXT_COPYAVLOCATION },
161 { 12, IDC_CONTENT_CONTEXT_COPYIMAGE },
162 { 13, IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB },
163 { 14, IDC_CONTENT_CONTEXT_OPENAVNEWTAB },
164 { 15, IDC_CONTENT_CONTEXT_PLAYPAUSE },
165 { 16, IDC_CONTENT_CONTEXT_MUTE },
166 { 17, IDC_CONTENT_CONTEXT_LOOP },
167 { 18, IDC_CONTENT_CONTEXT_CONTROLS },
168 { 19, IDC_CONTENT_CONTEXT_ROTATECW },
169 { 20, IDC_CONTENT_CONTEXT_ROTATECCW },
170 { 21, IDC_BACK },
171 { 22, IDC_FORWARD },
172 { 23, IDC_SAVE_PAGE },
173 { 24, IDC_RELOAD },
174 { 25, IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP },
175 { 26, IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP },
176 { 27, IDC_PRINT },
177 { 28, IDC_VIEW_SOURCE },
178 { 29, IDC_CONTENT_CONTEXT_INSPECTELEMENT },
179 { 30, IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE },
180 { 31, IDC_CONTENT_CONTEXT_VIEWPAGEINFO },
181 { 32, IDC_CONTENT_CONTEXT_TRANSLATE },
182 { 33, IDC_CONTENT_CONTEXT_RELOADFRAME },
183 { 34, IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE },
184 { 35, IDC_CONTENT_CONTEXT_VIEWFRAMEINFO },
185 { 36, IDC_CONTENT_CONTEXT_UNDO },
186 { 37, IDC_CONTENT_CONTEXT_REDO },
187 { 38, IDC_CONTENT_CONTEXT_CUT },
188 { 39, IDC_CONTENT_CONTEXT_COPY },
189 { 40, IDC_CONTENT_CONTEXT_PASTE },
190 { 41, IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE },
191 { 42, IDC_CONTENT_CONTEXT_DELETE },
192 { 43, IDC_CONTENT_CONTEXT_SELECTALL },
193 { 44, IDC_CONTENT_CONTEXT_SEARCHWEBFOR },
194 { 45, IDC_CONTENT_CONTEXT_GOTOURL },
195 { 46, IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS },
196 { 47, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS },
197 { 48, IDC_CONTENT_CONTEXT_ADDSEARCHENGINE },
198 { 49, IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES },
199 { 50, IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT },
200 { 51, IDC_SPEECH_INPUT_MENU },
201 { 52, IDC_CONTENT_CONTEXT_OPENLINKWITH },
202 { 53, IDC_CHECK_SPELLING_WHILE_TYPING },
203 { 54, IDC_SPELLCHECK_MENU },
204 { 55, IDC_CONTENT_CONTEXT_SPELLING_TOGGLE },
205 { 56, IDC_SPELLCHECK_LANGUAGES_FIRST },
206 { 57, IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE },
207 // Add new items here and use |enum_id| from the next line.
208 { 58, 0 }, // Must be the last. Increment |enum_id| when new IDC was added.
211 // Collapses large ranges of ids before looking for UMA enum.
212 int CollapleCommandsForUMA(int id) {
213 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
214 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
215 return IDC_CONTENT_CONTEXT_CUSTOM_FIRST;
218 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
219 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
220 return IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST;
223 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
224 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
225 return IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
228 if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
229 id <= IDC_SPELLCHECK_LANGUAGES_LAST) {
230 return IDC_SPELLCHECK_LANGUAGES_FIRST;
233 return id;
236 // Returns UMA enum value for command specified by |id| or -1 if not found.
237 int FindUMAEnumValueForCommand(int id) {
238 id = CollapleCommandsForUMA(id);
239 const size_t kMappingSize = arraysize(kUmaEnumToControlId);
240 for (size_t i = 0; i < kMappingSize; ++i) {
241 if (kUmaEnumToControlId[i].control_id == id) {
242 return kUmaEnumToControlId[i].enum_id;
245 return -1;
248 // Increments histogram value for used items specified by |id|.
249 void RecordUsedItem(int id) {
250 int enum_id = FindUMAEnumValueForCommand(id);
251 if (enum_id != -1) {
252 const size_t kMappingSize = arraysize(kUmaEnumToControlId);
253 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Used", enum_id,
254 kUmaEnumToControlId[kMappingSize - 1].enum_id);
255 } else {
256 NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id;
260 // Increments histogram value for visible context menu item specified by |id|.
261 void RecordShownItem(int id) {
262 int enum_id = FindUMAEnumValueForCommand(id);
263 if (enum_id != -1) {
264 const size_t kMappingSize = arraysize(kUmaEnumToControlId);
265 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Shown", enum_id,
266 kUmaEnumToControlId[kMappingSize - 1].enum_id);
267 } else {
268 // Just warning here. It's harder to maintain list of all possibly
269 // visible items than executable items.
270 DLOG(ERROR) << "Update kUmaEnumToControlId. Unhanded IDC: " << id;
274 // Usually a new tab is expected where this function is used,
275 // however users should be able to open a tab in background
276 // or in a new window.
277 WindowOpenDisposition ForceNewTabDispositionFromEventFlags(
278 int event_flags) {
279 WindowOpenDisposition disposition =
280 ui::DispositionFromEventFlags(event_flags);
281 return disposition == CURRENT_TAB ? NEW_FOREGROUND_TAB : disposition;
284 bool IsCustomItemEnabled(const std::vector<content::MenuItem>& items, int id) {
285 DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
286 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST);
287 for (size_t i = 0; i < items.size(); ++i) {
288 int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action;
289 if (action_id == id)
290 return items[i].enabled;
291 if (items[i].type == content::MenuItem::SUBMENU) {
292 if (IsCustomItemEnabled(items[i].submenu, id))
293 return true;
296 return false;
299 bool IsCustomItemChecked(const std::vector<content::MenuItem>& items, int id) {
300 DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
301 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST);
302 for (size_t i = 0; i < items.size(); ++i) {
303 int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action;
304 if (action_id == id)
305 return items[i].checked;
306 if (items[i].type == content::MenuItem::SUBMENU) {
307 if (IsCustomItemChecked(items[i].submenu, id))
308 return true;
311 return false;
314 const size_t kMaxCustomMenuDepth = 5;
315 const size_t kMaxCustomMenuTotalItems = 1000;
317 void AddCustomItemsToMenu(const std::vector<content::MenuItem>& items,
318 size_t depth,
319 size_t* total_items,
320 ui::SimpleMenuModel::Delegate* delegate,
321 ui::SimpleMenuModel* menu_model) {
322 if (depth > kMaxCustomMenuDepth) {
323 LOG(ERROR) << "Custom menu too deeply nested.";
324 return;
326 for (size_t i = 0; i < items.size(); ++i) {
327 if (IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action >=
328 IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
329 LOG(ERROR) << "Custom menu action value too big.";
330 return;
332 if (*total_items >= kMaxCustomMenuTotalItems) {
333 LOG(ERROR) << "Custom menu too large (too many items).";
334 return;
336 (*total_items)++;
337 switch (items[i].type) {
338 case content::MenuItem::OPTION:
339 menu_model->AddItem(
340 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
341 items[i].label);
342 break;
343 case content::MenuItem::CHECKABLE_OPTION:
344 menu_model->AddCheckItem(
345 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
346 items[i].label);
347 break;
348 case content::MenuItem::GROUP:
349 // TODO(viettrungluu): I don't know what this is supposed to do.
350 NOTREACHED();
351 break;
352 case content::MenuItem::SEPARATOR:
353 menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
354 break;
355 case content::MenuItem::SUBMENU: {
356 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate);
357 AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate,
358 submenu);
359 menu_model->AddSubMenu(
360 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
361 items[i].label,
362 submenu);
363 break;
365 default:
366 NOTREACHED();
367 break;
372 void DevToolsInspectElementAt(RenderViewHost* rvh, int x, int y) {
373 DevToolsWindow::InspectElement(rvh, x, y);
376 // Helper function to escape "&" as "&&".
377 void EscapeAmpersands(base::string16* text) {
378 const base::char16 ampersand[] = {'&', 0};
379 base::ReplaceChars(*text, ampersand, base::ASCIIToUTF16("&&"), text);
382 } // namespace
384 // static
385 const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50;
387 // static
388 bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) {
389 return url.SchemeIs(chrome::kChromeDevToolsScheme);
392 // static
393 bool RenderViewContextMenu::IsInternalResourcesURL(const GURL& url) {
394 if (!url.SchemeIs(content::kChromeUIScheme))
395 return false;
396 return url.host() == chrome::kChromeUISyncResourcesHost;
399 static const int kSpellcheckRadioGroup = 1;
401 RenderViewContextMenu::RenderViewContextMenu(
402 content::RenderFrameHost* render_frame_host,
403 const content::ContextMenuParams& params)
404 : params_(params),
405 source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)),
406 render_process_id_(render_frame_host->GetProcess()->GetID()),
407 render_frame_id_(render_frame_host->GetRoutingID()),
408 profile_(Profile::FromBrowserContext(
409 source_web_contents_->GetBrowserContext())),
410 menu_model_(this),
411 extension_items_(profile_, this, &menu_model_,
412 base::Bind(MenuItemMatchesParams, params_)),
413 speech_input_submenu_model_(this),
414 protocol_handler_submenu_model_(this),
415 protocol_handler_registry_(
416 ProtocolHandlerRegistryFactory::GetForProfile(profile_)),
417 command_executed_(false),
418 is_guest_(false) {
419 RenderViewHost* rvh = source_web_contents_->GetRenderViewHost();
420 if (rvh && rvh->GetProcess()->IsGuest())
421 is_guest_ = true;
424 RenderViewContextMenu::~RenderViewContextMenu() {
427 // Menu construction functions -------------------------------------------------
429 void RenderViewContextMenu::Init() {
430 InitMenu();
431 PlatformInit();
434 void RenderViewContextMenu::Cancel() {
435 PlatformCancel();
438 static bool ExtensionPatternMatch(const extensions::URLPatternSet& patterns,
439 const GURL& url) {
440 // No patterns means no restriction, so that implicitly matches.
441 if (patterns.is_empty())
442 return true;
443 return patterns.MatchesURL(url);
446 // static
447 bool RenderViewContextMenu::ExtensionContextAndPatternMatch(
448 const content::ContextMenuParams& params,
449 MenuItem::ContextList contexts,
450 const extensions::URLPatternSet& target_url_patterns) {
451 const bool has_link = !params.link_url.is_empty();
452 const bool has_selection = !params.selection_text.empty();
453 const bool in_frame = !params.frame_url.is_empty();
455 if (contexts.Contains(MenuItem::ALL) ||
456 (has_selection && contexts.Contains(MenuItem::SELECTION)) ||
457 (params.is_editable && contexts.Contains(MenuItem::EDITABLE)) ||
458 (in_frame && contexts.Contains(MenuItem::FRAME)))
459 return true;
461 if (has_link && contexts.Contains(MenuItem::LINK) &&
462 ExtensionPatternMatch(target_url_patterns, params.link_url))
463 return true;
465 switch (params.media_type) {
466 case WebContextMenuData::MediaTypeImage:
467 if (contexts.Contains(MenuItem::IMAGE) &&
468 ExtensionPatternMatch(target_url_patterns, params.src_url))
469 return true;
470 break;
472 case WebContextMenuData::MediaTypeVideo:
473 if (contexts.Contains(MenuItem::VIDEO) &&
474 ExtensionPatternMatch(target_url_patterns, params.src_url))
475 return true;
476 break;
478 case WebContextMenuData::MediaTypeAudio:
479 if (contexts.Contains(MenuItem::AUDIO) &&
480 ExtensionPatternMatch(target_url_patterns, params.src_url))
481 return true;
482 break;
484 default:
485 break;
488 // PAGE is the least specific context, so we only examine that if none of the
489 // other contexts apply (except for FRAME, which is included in PAGE for
490 // backwards compatibility).
491 if (!has_link && !has_selection && !params.is_editable &&
492 params.media_type == WebContextMenuData::MediaTypeNone &&
493 contexts.Contains(MenuItem::PAGE))
494 return true;
496 return false;
499 static const GURL& GetDocumentURL(const content::ContextMenuParams& params) {
500 return params.frame_url.is_empty() ? params.page_url : params.frame_url;
503 // static
504 bool RenderViewContextMenu::MenuItemMatchesParams(
505 const content::ContextMenuParams& params,
506 const extensions::MenuItem* item) {
507 bool match = ExtensionContextAndPatternMatch(params, item->contexts(),
508 item->target_url_patterns());
509 if (!match)
510 return false;
512 const GURL& document_url = GetDocumentURL(params);
513 return ExtensionPatternMatch(item->document_url_patterns(), document_url);
516 void RenderViewContextMenu::AppendAllExtensionItems() {
517 extension_items_.Clear();
518 ExtensionService* service =
519 extensions::ExtensionSystem::Get(profile_)->extension_service();
520 if (!service)
521 return; // In unit-tests, we may not have an ExtensionService.
523 MenuManager* menu_manager = MenuManager::Get(profile_);
524 if (!menu_manager)
525 return;
527 base::string16 printable_selection_text = PrintableSelectionText();
528 EscapeAmpersands(&printable_selection_text);
530 // Get a list of extension id's that have context menu items, and sort by the
531 // top level context menu title of the extension.
532 std::set<std::string> ids = menu_manager->ExtensionIds();
533 std::vector<base::string16> sorted_menu_titles;
534 std::map<base::string16, std::string> map_ids;
535 for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) {
536 const Extension* extension = service->GetExtensionById(*i, false);
537 // Platform apps have their context menus created directly in
538 // AppendPlatformAppItems.
539 if (extension && !extension->is_platform_app()) {
540 base::string16 menu_title = extension_items_.GetTopLevelContextMenuTitle(
541 *i, printable_selection_text);
542 map_ids[menu_title] = *i;
543 sorted_menu_titles.push_back(menu_title);
546 if (sorted_menu_titles.empty())
547 return;
549 const std::string app_locale = g_browser_process->GetApplicationLocale();
550 l10n_util::SortStrings16(app_locale, &sorted_menu_titles);
552 int index = 0;
553 base::TimeTicks begin = base::TimeTicks::Now();
554 for (size_t i = 0; i < sorted_menu_titles.size(); ++i) {
555 const std::string& id = map_ids[sorted_menu_titles[i]];
556 extension_items_.AppendExtensionItems(id, printable_selection_text,
557 &index);
560 UMA_HISTOGRAM_TIMES("Extensions.ContextMenus_BuildTime",
561 base::TimeTicks::Now() - begin);
562 UMA_HISTOGRAM_COUNTS("Extensions.ContextMenus_ItemCount", index);
565 void RenderViewContextMenu::InitMenu() {
566 if (chrome::IsRunningInForcedAppMode()) {
567 AppendAppModeItems();
568 return;
571 extensions::ViewType view_type =
572 extensions::GetViewType(source_web_contents_);
573 if (view_type == extensions::VIEW_TYPE_APP_SHELL) {
574 AppendPlatformAppItems();
575 return;
576 } else if (view_type == extensions::VIEW_TYPE_EXTENSION_POPUP) {
577 AppendPopupExtensionItems();
578 return;
579 } else if (view_type == extensions::VIEW_TYPE_PANEL) {
580 AppendPanelItems();
581 return;
584 const bool has_link = !params_.unfiltered_link_url.is_empty();
585 const bool has_selection = !params_.selection_text.empty();
587 if (AppendCustomItems()) {
588 // If there's a selection, don't early return when there are custom items,
589 // but fall through to adding the normal ones after the custom ones.
590 if (has_selection) {
591 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
592 } else {
593 // Don't add items for Pepper menu.
594 if (!params_.custom_context.is_pepper_menu)
595 AppendDeveloperItems();
596 return;
600 // When no special node or text is selected and selection has no link,
601 // show page items.
602 if (params_.media_type == WebContextMenuData::MediaTypeNone &&
603 !has_link &&
604 !params_.is_editable &&
605 !is_guest_ &&
606 !has_selection) {
607 if (!params_.page_url.is_empty()) {
608 bool is_devtools = IsDevToolsURL(params_.page_url);
609 if (!is_devtools && !IsInternalResourcesURL(params_.page_url)) {
610 AppendPageItems();
611 // Merge in frame items if we clicked within a frame that needs them.
612 if (!params_.frame_url.is_empty()) {
613 is_devtools = IsDevToolsURL(params_.frame_url);
614 if (!is_devtools && !IsInternalResourcesURL(params_.frame_url)) {
615 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
616 AppendFrameItems();
620 } else {
621 DCHECK(params_.frame_url.is_empty());
625 // Do not show link related items for guest.
626 if (has_link && !is_guest_) {
627 AppendLinkItems();
628 if (params_.media_type != WebContextMenuData::MediaTypeNone)
629 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
632 switch (params_.media_type) {
633 case WebContextMenuData::MediaTypeNone:
634 break;
635 case WebContextMenuData::MediaTypeImage:
636 AppendImageItems();
637 break;
638 case WebContextMenuData::MediaTypeVideo:
639 AppendVideoItems();
640 break;
641 case WebContextMenuData::MediaTypeAudio:
642 AppendAudioItems();
643 break;
644 case WebContextMenuData::MediaTypePlugin:
645 AppendPluginItems();
646 break;
647 #ifdef WEBCONTEXT_MEDIATYPEFILE_DEFINED
648 case WebContextMenuData::MediaTypeFile:
649 break;
650 #endif
653 if (params_.is_editable)
654 AppendEditableItems();
655 else if (has_selection)
656 AppendCopyItem();
658 if (!is_guest_ && has_selection) {
659 AppendSearchProvider();
660 if (!IsDevToolsURL(params_.page_url))
661 AppendPrintItem();
664 if (!IsDevToolsURL(params_.page_url) && !is_guest_)
665 AppendAllExtensionItems();
667 AppendDeveloperItems();
669 if (!is_guest_) {
670 #if defined(ENABLE_FULL_PRINTING)
671 if (!print_preview_menu_observer_.get()) {
672 print_preview_menu_observer_.reset(
673 new PrintPreviewContextMenuObserver(source_web_contents_));
676 observers_.AddObserver(print_preview_menu_observer_.get());
677 #endif
681 const Extension* RenderViewContextMenu::GetExtension() const {
682 extensions::ExtensionSystem* system =
683 extensions::ExtensionSystem::Get(profile_);
684 // There is no process manager in some tests.
685 if (!system->process_manager())
686 return NULL;
688 return system->process_manager()->GetExtensionForRenderViewHost(
689 source_web_contents_->GetRenderViewHost());
692 void RenderViewContextMenu::AppendAppModeItems() {
693 const bool has_selection = !params_.selection_text.empty();
695 if (params_.is_editable)
696 AppendEditableItems();
697 else if (has_selection)
698 AppendCopyItem();
701 void RenderViewContextMenu::AppendPlatformAppItems() {
702 const Extension* platform_app = GetExtension();
704 // The RVH might be for a process sandboxed from the extension.
705 if (!platform_app)
706 return;
708 DCHECK(platform_app->is_platform_app());
710 const bool has_selection = !params_.selection_text.empty();
712 // Add undo/redo, cut/copy/paste etc for text fields.
713 if (params_.is_editable)
714 AppendEditableItems();
715 else if (has_selection)
716 AppendCopyItem();
718 int index = 0;
719 extension_items_.AppendExtensionItems(platform_app->id(),
720 PrintableSelectionText(), &index);
722 // Add dev tools for unpacked extensions.
723 if (extensions::Manifest::IsUnpackedLocation(platform_app->location()) ||
724 CommandLine::ForCurrentProcess()->HasSwitch(
725 switches::kDebugPackedApps)) {
726 // Add a separator if there are any items already in the menu.
727 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
729 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP,
730 IDS_CONTENT_CONTEXT_RELOAD_PACKAGED_APP);
731 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP,
732 IDS_CONTENT_CONTEXT_RESTART_APP);
733 AppendDeveloperItems();
734 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE,
735 IDS_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE);
739 void RenderViewContextMenu::AppendPopupExtensionItems() {
740 const bool has_selection = !params_.selection_text.empty();
742 if (params_.is_editable)
743 AppendEditableItems();
744 else if (has_selection)
745 AppendCopyItem();
747 if (has_selection)
748 AppendSearchProvider();
750 AppendAllExtensionItems();
751 AppendDeveloperItems();
754 void RenderViewContextMenu::AppendPanelItems() {
755 bool has_selection = !params_.selection_text.empty();
757 // Checking link should take precedence before checking selection since on Mac
758 // right-clicking a link will also make it selected.
759 if (params_.unfiltered_link_url.is_valid())
760 AppendLinkItems();
762 if (params_.is_editable)
763 AppendEditableItems();
764 else if (has_selection)
765 AppendCopyItem();
767 // Avoid appending extension related items when |extension| is null. This
768 // happens when the panel is navigated to a url outside of the extension's
769 // package.
770 const Extension* extension = GetExtension();
771 if (extension) {
772 // Only add extension items from this extension.
773 int index = 0;
774 extension_items_.AppendExtensionItems(extension->id(),
775 PrintableSelectionText(), &index);
779 void RenderViewContextMenu::AddMenuItem(int command_id,
780 const base::string16& title) {
781 menu_model_.AddItem(command_id, title);
784 void RenderViewContextMenu::AddCheckItem(int command_id,
785 const base::string16& title) {
786 menu_model_.AddCheckItem(command_id, title);
789 void RenderViewContextMenu::AddSeparator() {
790 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
793 void RenderViewContextMenu::AddSubMenu(int command_id,
794 const base::string16& label,
795 ui::MenuModel* model) {
796 menu_model_.AddSubMenu(command_id, label, model);
799 void RenderViewContextMenu::UpdateMenuItem(int command_id,
800 bool enabled,
801 bool hidden,
802 const base::string16& label) {
803 // This function needs platform-specific implementation.
804 NOTIMPLEMENTED();
807 RenderViewHost* RenderViewContextMenu::GetRenderViewHost() const {
808 return source_web_contents_->GetRenderViewHost();
811 WebContents* RenderViewContextMenu::GetWebContents() const {
812 return source_web_contents_;
815 Profile* RenderViewContextMenu::GetProfile() const {
816 return profile_;
819 bool RenderViewContextMenu::AppendCustomItems() {
820 size_t total_items = 0;
821 AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this,
822 &menu_model_);
823 return total_items > 0;
826 void RenderViewContextMenu::AppendDeveloperItems() {
827 // Show Inspect Element in DevTools itself only in case of the debug
828 // devtools build.
829 bool show_developer_items = !IsDevToolsURL(params_.page_url);
831 #if defined(DEBUG_DEVTOOLS)
832 show_developer_items = true;
833 #endif
835 if (!show_developer_items)
836 return;
838 // In the DevTools popup menu, "developer items" is normally the only
839 // section, so omit the separator there.
840 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
841 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTELEMENT,
842 IDS_CONTENT_CONTEXT_INSPECTELEMENT);
845 void RenderViewContextMenu::AppendLinkItems() {
846 if (!params_.link_url.is_empty()) {
847 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB,
848 IDS_CONTENT_CONTEXT_OPENLINKNEWTAB);
849 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW,
850 IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW);
851 if (params_.link_url.is_valid()) {
852 AppendProtocolHandlerSubMenu();
855 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD,
856 IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD);
857 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVELINKAS,
858 IDS_CONTENT_CONTEXT_SAVELINKAS);
861 menu_model_.AddItemWithStringId(
862 IDC_CONTENT_CONTEXT_COPYLINKLOCATION,
863 params_.link_url.SchemeIs(content::kMailToScheme) ?
864 IDS_CONTENT_CONTEXT_COPYEMAILADDRESS :
865 IDS_CONTENT_CONTEXT_COPYLINKLOCATION);
868 void RenderViewContextMenu::AppendImageItems() {
869 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS,
870 IDS_CONTENT_CONTEXT_SAVEIMAGEAS);
871 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION,
872 IDS_CONTENT_CONTEXT_COPYIMAGELOCATION);
873 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGE,
874 IDS_CONTENT_CONTEXT_COPYIMAGE);
875 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB,
876 IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB);
877 const TemplateURL* const default_provider =
878 TemplateURLServiceFactory::GetForProfile(profile_)->
879 GetDefaultSearchProvider();
880 if (!is_guest_ && params_.has_image_contents && default_provider &&
881 !default_provider->image_url().empty() &&
882 default_provider->image_url_ref().IsValid()) {
883 menu_model_.AddItem(
884 IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
885 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
886 default_provider->short_name()));
888 AppendPrintItem();
891 void RenderViewContextMenu::AppendAudioItems() {
892 AppendMediaItems();
893 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
894 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
895 IDS_CONTENT_CONTEXT_SAVEAUDIOAS);
896 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION,
897 IDS_CONTENT_CONTEXT_COPYAUDIOLOCATION);
898 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB,
899 IDS_CONTENT_CONTEXT_OPENAUDIONEWTAB);
902 void RenderViewContextMenu::AppendVideoItems() {
903 AppendMediaItems();
904 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
905 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
906 IDS_CONTENT_CONTEXT_SAVEVIDEOAS);
907 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION,
908 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION);
909 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB,
910 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB);
913 void RenderViewContextMenu::AppendMediaItems() {
914 int media_flags = params_.media_flags;
916 menu_model_.AddItemWithStringId(
917 IDC_CONTENT_CONTEXT_PLAYPAUSE,
918 media_flags & WebContextMenuData::MediaPaused ?
919 IDS_CONTENT_CONTEXT_PLAY :
920 IDS_CONTENT_CONTEXT_PAUSE);
922 menu_model_.AddItemWithStringId(
923 IDC_CONTENT_CONTEXT_MUTE,
924 media_flags & WebContextMenuData::MediaMuted ?
925 IDS_CONTENT_CONTEXT_UNMUTE :
926 IDS_CONTENT_CONTEXT_MUTE);
928 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_LOOP,
929 IDS_CONTENT_CONTEXT_LOOP);
930 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_CONTROLS,
931 IDS_CONTENT_CONTEXT_CONTROLS);
934 void RenderViewContextMenu::AppendPluginItems() {
935 if (params_.page_url == params_.src_url) {
936 // Full page plugin, so show page menu items.
937 if (params_.link_url.is_empty() && params_.selection_text.empty())
938 AppendPageItems();
939 } else {
940 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
941 IDS_CONTENT_CONTEXT_SAVEPAGEAS);
942 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
945 if (params_.media_flags & WebContextMenuData::MediaCanRotate) {
946 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
947 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECW,
948 IDS_CONTENT_CONTEXT_ROTATECW);
949 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECCW,
950 IDS_CONTENT_CONTEXT_ROTATECCW);
954 void RenderViewContextMenu::AppendPageItems() {
955 menu_model_.AddItemWithStringId(IDC_BACK, IDS_CONTENT_CONTEXT_BACK);
956 menu_model_.AddItemWithStringId(IDC_FORWARD, IDS_CONTENT_CONTEXT_FORWARD);
957 menu_model_.AddItemWithStringId(IDC_RELOAD, IDS_CONTENT_CONTEXT_RELOAD);
958 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
959 menu_model_.AddItemWithStringId(IDC_SAVE_PAGE,
960 IDS_CONTENT_CONTEXT_SAVEPAGEAS);
961 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
963 if (TranslateManager::IsTranslatableURL(params_.page_url)) {
964 std::string locale = g_browser_process->GetApplicationLocale();
965 locale = TranslateDownloadManager::GetLanguageCode(locale);
966 base::string16 language =
967 l10n_util::GetDisplayNameForLocale(locale, locale, true);
968 menu_model_.AddItem(
969 IDC_CONTENT_CONTEXT_TRANSLATE,
970 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE, language));
973 menu_model_.AddItemWithStringId(IDC_VIEW_SOURCE,
974 IDS_CONTENT_CONTEXT_VIEWPAGESOURCE);
975 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWPAGEINFO,
976 IDS_CONTENT_CONTEXT_VIEWPAGEINFO);
979 void RenderViewContextMenu::AppendFrameItems() {
980 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOADFRAME,
981 IDS_CONTENT_CONTEXT_RELOADFRAME);
982 // These two menu items have yet to be implemented.
983 // http://code.google.com/p/chromium/issues/detail?id=11827
984 // IDS_CONTENT_CONTEXT_SAVEFRAMEAS
985 // IDS_CONTENT_CONTEXT_PRINTFRAME
986 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE,
987 IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE);
988 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMEINFO,
989 IDS_CONTENT_CONTEXT_VIEWFRAMEINFO);
992 void RenderViewContextMenu::AppendCopyItem() {
993 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY,
994 IDS_CONTENT_CONTEXT_COPY);
997 void RenderViewContextMenu::AppendPrintItem() {
998 if (profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) &&
999 (params_.media_type == WebContextMenuData::MediaTypeNone ||
1000 params_.media_flags & WebContextMenuData::MediaCanPrint)) {
1001 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
1005 void RenderViewContextMenu::AppendSearchProvider() {
1006 DCHECK(profile_);
1008 TrimWhitespace(params_.selection_text, TRIM_ALL, &params_.selection_text);
1009 if (params_.selection_text.empty())
1010 return;
1012 base::ReplaceChars(params_.selection_text, AutocompleteMatch::kInvalidChars,
1013 base::ASCIIToUTF16(" "), &params_.selection_text);
1015 AutocompleteMatch match;
1016 AutocompleteClassifierFactory::GetForProfile(profile_)->Classify(
1017 params_.selection_text, false, false, AutocompleteInput::INVALID_SPEC,
1018 &match, NULL);
1019 selection_navigation_url_ = match.destination_url;
1020 if (!selection_navigation_url_.is_valid())
1021 return;
1023 base::string16 printable_selection_text = PrintableSelectionText();
1024 EscapeAmpersands(&printable_selection_text);
1026 if (AutocompleteMatch::IsSearchType(match.type)) {
1027 const TemplateURL* const default_provider =
1028 TemplateURLServiceFactory::GetForProfile(profile_)->
1029 GetDefaultSearchProvider();
1030 if (!default_provider)
1031 return;
1032 menu_model_.AddItem(
1033 IDC_CONTENT_CONTEXT_SEARCHWEBFOR,
1034 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR,
1035 default_provider->short_name(),
1036 printable_selection_text));
1037 } else {
1038 if ((selection_navigation_url_ != params_.link_url) &&
1039 ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1040 selection_navigation_url_.scheme())) {
1041 menu_model_.AddItem(
1042 IDC_CONTENT_CONTEXT_GOTOURL,
1043 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_GOTOURL,
1044 printable_selection_text));
1049 void RenderViewContextMenu::AppendEditableItems() {
1050 const bool use_spellcheck_and_search = !chrome::IsRunningInForcedAppMode();
1052 if (use_spellcheck_and_search)
1053 AppendSpellingSuggestionsSubMenu();
1055 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO,
1056 IDS_CONTENT_CONTEXT_UNDO);
1057 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO,
1058 IDS_CONTENT_CONTEXT_REDO);
1059 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1060 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_CUT,
1061 IDS_CONTENT_CONTEXT_CUT);
1062 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY,
1063 IDS_CONTENT_CONTEXT_COPY);
1064 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE,
1065 IDS_CONTENT_CONTEXT_PASTE);
1066 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE,
1067 IDS_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE);
1068 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_DELETE,
1069 IDS_CONTENT_CONTEXT_DELETE);
1070 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1072 if (use_spellcheck_and_search && !params_.keyword_url.is_empty()) {
1073 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ADDSEARCHENGINE,
1074 IDS_CONTENT_CONTEXT_ADDSEARCHENGINE);
1075 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1078 if (use_spellcheck_and_search)
1079 AppendSpellcheckOptionsSubMenu();
1080 AppendSpeechInputOptionsSubMenu();
1081 AppendPlatformEditableItems();
1083 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1084 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SELECTALL,
1085 IDS_CONTENT_CONTEXT_SELECTALL);
1088 void RenderViewContextMenu::AppendSpellingSuggestionsSubMenu() {
1089 if (!spelling_menu_observer_.get())
1090 spelling_menu_observer_.reset(new SpellingMenuObserver(this));
1091 observers_.AddObserver(spelling_menu_observer_.get());
1092 spelling_menu_observer_->InitMenu(params_);
1095 void RenderViewContextMenu::AppendSpellcheckOptionsSubMenu() {
1096 if (!spellchecker_submenu_observer_.get()) {
1097 spellchecker_submenu_observer_.reset(new SpellCheckerSubMenuObserver(
1098 this, this, kSpellcheckRadioGroup));
1100 spellchecker_submenu_observer_->InitMenu(params_);
1101 observers_.AddObserver(spellchecker_submenu_observer_.get());
1104 void RenderViewContextMenu::AppendSpeechInputOptionsSubMenu() {
1105 if (params_.speech_input_enabled) {
1106 speech_input_submenu_model_.AddCheckItem(
1107 IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES,
1108 l10n_util::GetStringUTF16(
1109 IDS_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES));
1111 speech_input_submenu_model_.AddItemWithStringId(
1112 IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT,
1113 IDS_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT);
1115 menu_model_.AddSubMenu(
1116 IDC_SPEECH_INPUT_MENU,
1117 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPEECH_INPUT_MENU),
1118 &speech_input_submenu_model_);
1122 void RenderViewContextMenu::AppendProtocolHandlerSubMenu() {
1123 const ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1124 GetHandlersForLinkUrl();
1125 if (handlers.empty())
1126 return;
1127 size_t max = IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST -
1128 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
1129 for (size_t i = 0; i < handlers.size() && i <= max; i++) {
1130 protocol_handler_submenu_model_.AddItem(
1131 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST + i,
1132 handlers[i].title());
1134 protocol_handler_submenu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1135 protocol_handler_submenu_model_.AddItem(
1136 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS,
1137 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH_CONFIGURE));
1139 menu_model_.AddSubMenu(
1140 IDC_CONTENT_CONTEXT_OPENLINKWITH,
1141 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH),
1142 &protocol_handler_submenu_model_);
1145 void RenderViewContextMenu::AppendPlatformEditableItems() {
1148 // Menu delegate functions -----------------------------------------------------
1150 bool RenderViewContextMenu::IsCommandIdEnabled(int id) const {
1151 // If this command is is added by one of our observers, we dispatch it to the
1152 // observer.
1153 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
1154 RenderViewContextMenuObserver* observer;
1155 while ((observer = it.GetNext()) != NULL) {
1156 if (observer->IsCommandIdSupported(id))
1157 return observer->IsCommandIdEnabled(id);
1160 CoreTabHelper* core_tab_helper =
1161 CoreTabHelper::FromWebContents(source_web_contents_);
1162 int content_restrictions = 0;
1163 if (core_tab_helper)
1164 content_restrictions = core_tab_helper->content_restrictions();
1165 if (id == IDC_PRINT && (content_restrictions & CONTENT_RESTRICTION_PRINT))
1166 return false;
1168 if (id == IDC_SAVE_PAGE &&
1169 (content_restrictions & CONTENT_RESTRICTION_SAVE)) {
1170 return false;
1173 // Allow Spell Check language items on sub menu for text area context menu.
1174 if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) &&
1175 (id < IDC_SPELLCHECK_LANGUAGES_LAST)) {
1176 return profile_->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck);
1179 // Custom items.
1180 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1181 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1182 return IsCustomItemEnabled(params_.custom_items, id);
1185 // Extension items.
1186 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
1187 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
1188 return extension_items_.IsCommandIdEnabled(id);
1191 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
1192 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
1193 return true;
1196 IncognitoModePrefs::Availability incognito_avail =
1197 IncognitoModePrefs::GetAvailability(profile_->GetPrefs());
1198 switch (id) {
1199 case IDC_BACK:
1200 return source_web_contents_->GetController().CanGoBack();
1202 case IDC_FORWARD:
1203 return source_web_contents_->GetController().CanGoForward();
1205 case IDC_RELOAD: {
1206 CoreTabHelper* core_tab_helper =
1207 CoreTabHelper::FromWebContents(source_web_contents_);
1208 if (!core_tab_helper)
1209 return false;
1211 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
1212 return !core_delegate ||
1213 core_delegate->CanReloadContents(source_web_contents_);
1216 case IDC_VIEW_SOURCE:
1217 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
1218 return source_web_contents_->GetController().CanViewSource();
1220 case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
1221 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE:
1222 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP:
1223 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP:
1224 return IsDevCommandEnabled(id);
1226 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO:
1227 if (source_web_contents_->GetController().GetActiveEntry() == NULL)
1228 return false;
1229 // Disabled if no browser is associated (e.g. desktop notifications).
1230 if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL)
1231 return false;
1232 return true;
1234 case IDC_CONTENT_CONTEXT_TRANSLATE: {
1235 TranslateTabHelper* translate_tab_helper =
1236 TranslateTabHelper::FromWebContents(source_web_contents_);
1237 if (!translate_tab_helper)
1238 return false;
1239 std::string original_lang =
1240 translate_tab_helper->GetLanguageState().original_language();
1241 std::string target_lang = g_browser_process->GetApplicationLocale();
1242 target_lang = TranslateDownloadManager::GetLanguageCode(target_lang);
1243 // Note that we intentionally enable the menu even if the original and
1244 // target languages are identical. This is to give a way to user to
1245 // translate a page that might contains text fragments in a different
1246 // language.
1247 return ((params_.edit_flags & WebContextMenuData::CanTranslate) != 0) &&
1248 !original_lang.empty() && // Did we receive the page language yet?
1249 !translate_tab_helper->GetLanguageState().IsPageTranslated() &&
1250 !source_web_contents_->GetInterstitialPage() &&
1251 // There are some application locales which can't be used as a
1252 // target language for translation.
1253 TranslateDownloadManager::IsSupportedLanguage(target_lang) &&
1254 // Disable on the Instant Extended NTP.
1255 !chrome::IsInstantNTP(source_web_contents_);
1258 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB:
1259 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
1260 return params_.link_url.is_valid();
1262 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION:
1263 return params_.unfiltered_link_url.is_valid();
1265 case IDC_CONTENT_CONTEXT_SAVELINKAS: {
1266 PrefService* local_state = g_browser_process->local_state();
1267 DCHECK(local_state);
1268 // Test if file-selection dialogs are forbidden by policy.
1269 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1270 return false;
1272 return params_.link_url.is_valid() &&
1273 ProfileIOData::IsHandledProtocol(params_.link_url.scheme());
1276 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: {
1277 PrefService* local_state = g_browser_process->local_state();
1278 DCHECK(local_state);
1279 // Test if file-selection dialogs are forbidden by policy.
1280 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1281 return false;
1283 return params_.src_url.is_valid() &&
1284 ProfileIOData::IsHandledProtocol(params_.src_url.scheme());
1287 // The images shown in the most visited thumbnails can't be opened or
1288 // searched for conventionally.
1289 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
1290 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
1291 return params_.src_url.is_valid() &&
1292 (params_.src_url.scheme() != content::kChromeUIScheme);
1294 case IDC_CONTENT_CONTEXT_COPYIMAGE:
1295 return params_.has_image_contents;
1297 // Media control commands should all be disabled if the player is in an
1298 // error state.
1299 case IDC_CONTENT_CONTEXT_PLAYPAUSE:
1300 case IDC_CONTENT_CONTEXT_LOOP:
1301 return (params_.media_flags &
1302 WebContextMenuData::MediaInError) == 0;
1304 // Mute and unmute should also be disabled if the player has no audio.
1305 case IDC_CONTENT_CONTEXT_MUTE:
1306 return (params_.media_flags &
1307 WebContextMenuData::MediaHasAudio) != 0 &&
1308 (params_.media_flags &
1309 WebContextMenuData::MediaInError) == 0;
1311 // Media controls can be toggled only for video player. If we toggle
1312 // controls for audio then the player disappears, and there is no way to
1313 // return it back.
1314 case IDC_CONTENT_CONTEXT_CONTROLS:
1315 return (params_.media_flags &
1316 WebContextMenuData::MediaHasVideo) != 0;
1318 case IDC_CONTENT_CONTEXT_ROTATECW:
1319 case IDC_CONTENT_CONTEXT_ROTATECCW:
1320 return
1321 (params_.media_flags & WebContextMenuData::MediaCanRotate) != 0;
1323 case IDC_CONTENT_CONTEXT_COPYAVLOCATION:
1324 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION:
1325 return params_.src_url.is_valid();
1327 case IDC_CONTENT_CONTEXT_SAVEAVAS: {
1328 PrefService* local_state = g_browser_process->local_state();
1329 DCHECK(local_state);
1330 // Test if file-selection dialogs are forbidden by policy.
1331 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1332 return false;
1334 const GURL& url = params_.src_url;
1335 bool can_save =
1336 (params_.media_flags & WebContextMenuData::MediaCanSave) &&
1337 url.is_valid() && ProfileIOData::IsHandledProtocol(url.scheme());
1338 #if defined(ENABLE_FULL_PRINTING)
1339 // Do not save the preview PDF on the print preview page.
1340 can_save = can_save &&
1341 !(printing::PrintPreviewDialogController::IsPrintPreviewURL(url));
1342 #endif
1343 return can_save;
1346 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
1347 return true;
1349 case IDC_SAVE_PAGE: {
1350 CoreTabHelper* core_tab_helper =
1351 CoreTabHelper::FromWebContents(source_web_contents_);
1352 if (!core_tab_helper)
1353 return false;
1355 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
1356 if (core_delegate &&
1357 !core_delegate->CanSaveContents(source_web_contents_))
1358 return false;
1360 PrefService* local_state = g_browser_process->local_state();
1361 DCHECK(local_state);
1362 // Test if file-selection dialogs are forbidden by policy.
1363 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1364 return false;
1366 // Instead of using GetURL here, we use url() (which is the "real" url of
1367 // the page) from the NavigationEntry because its reflects their origin
1368 // rather than the display one (returned by GetURL) which may be
1369 // different (like having "view-source:" on the front).
1370 // TODO(nasko): Audit all GetActiveEntry calls in this file.
1371 NavigationEntry* active_entry =
1372 source_web_contents_->GetController().GetActiveEntry();
1373 return content::IsSavableURL(
1374 (active_entry) ? active_entry->GetURL() : GURL());
1377 case IDC_CONTENT_CONTEXT_RELOADFRAME:
1378 return params_.frame_url.is_valid();
1380 case IDC_CONTENT_CONTEXT_UNDO:
1381 return !!(params_.edit_flags & WebContextMenuData::CanUndo);
1383 case IDC_CONTENT_CONTEXT_REDO:
1384 return !!(params_.edit_flags & WebContextMenuData::CanRedo);
1386 case IDC_CONTENT_CONTEXT_CUT:
1387 return !!(params_.edit_flags & WebContextMenuData::CanCut);
1389 case IDC_CONTENT_CONTEXT_COPY:
1390 return !!(params_.edit_flags & WebContextMenuData::CanCopy);
1392 case IDC_CONTENT_CONTEXT_PASTE:
1393 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE:
1394 return !!(params_.edit_flags & WebContextMenuData::CanPaste);
1396 case IDC_CONTENT_CONTEXT_DELETE:
1397 return !!(params_.edit_flags & WebContextMenuData::CanDelete);
1399 case IDC_CONTENT_CONTEXT_SELECTALL:
1400 return !!(params_.edit_flags & WebContextMenuData::CanSelectAll);
1402 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
1403 return !profile_->IsOffTheRecord() && params_.link_url.is_valid() &&
1404 incognito_avail != IncognitoModePrefs::DISABLED;
1406 case IDC_PRINT:
1407 return profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) &&
1408 (params_.media_type == WebContextMenuData::MediaTypeNone ||
1409 params_.media_flags & WebContextMenuData::MediaCanPrint);
1411 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR:
1412 case IDC_CONTENT_CONTEXT_GOTOURL:
1413 case IDC_SPELLPANEL_TOGGLE:
1414 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS:
1415 return true;
1416 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO:
1417 // Disabled if no browser is associated (e.g. desktop notifications).
1418 if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL)
1419 return false;
1420 return true;
1422 case IDC_CHECK_SPELLING_WHILE_TYPING:
1423 return profile_->GetPrefs()->GetBoolean(
1424 prefs::kEnableContinuousSpellcheck);
1426 #if !defined(OS_MACOSX) && defined(OS_POSIX)
1427 // TODO(suzhe): this should not be enabled for password fields.
1428 case IDC_INPUT_METHODS_MENU:
1429 return true;
1430 #endif
1432 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE:
1433 return !params_.keyword_url.is_empty();
1435 case IDC_SPELLCHECK_MENU:
1436 return true;
1438 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES:
1439 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT:
1440 case IDC_SPEECH_INPUT_MENU:
1441 return true;
1443 case IDC_CONTENT_CONTEXT_OPENLINKWITH:
1444 return true;
1446 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS:
1447 return true;
1449 default:
1450 NOTREACHED();
1451 return false;
1455 bool RenderViewContextMenu::IsCommandIdChecked(int id) const {
1456 // If this command is is added by one of our observers, we dispatch it to the
1457 // observer.
1458 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
1459 RenderViewContextMenuObserver* observer;
1460 while ((observer = it.GetNext()) != NULL) {
1461 if (observer->IsCommandIdSupported(id))
1462 return observer->IsCommandIdChecked(id);
1465 // See if the video is set to looping.
1466 if (id == IDC_CONTENT_CONTEXT_LOOP) {
1467 return (params_.media_flags &
1468 WebContextMenuData::MediaLoop) != 0;
1471 if (id == IDC_CONTENT_CONTEXT_CONTROLS) {
1472 return (params_.media_flags &
1473 WebContextMenuData::MediaControls) != 0;
1476 // Custom items.
1477 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1478 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1479 return IsCustomItemChecked(params_.custom_items, id);
1482 // Extension items.
1483 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
1484 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
1485 return extension_items_.IsCommandIdChecked(id);
1488 #if defined(ENABLE_INPUT_SPEECH)
1489 // Check box for menu item 'Block offensive words'.
1490 if (id == IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES) {
1491 return profile_->GetPrefs()->GetBoolean(
1492 prefs::kSpeechRecognitionFilterProfanities);
1494 #endif
1496 return false;
1499 void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) {
1500 command_executed_ = true;
1501 // If this command is is added by one of our observers, we dispatch it to the
1502 // observer.
1503 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
1504 RenderViewContextMenuObserver* observer;
1505 while ((observer = it.GetNext()) != NULL) {
1506 if (observer->IsCommandIdSupported(id))
1507 return observer->ExecuteCommand(id);
1510 RecordUsedItem(id);
1512 RenderViewHost* rvh = source_web_contents_->GetRenderViewHost();
1514 // Process custom actions range.
1515 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1516 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1517 unsigned action = id - IDC_CONTENT_CONTEXT_CUSTOM_FIRST;
1518 const content::CustomContextMenuContext& context = params_.custom_context;
1519 #if defined(ENABLE_PLUGINS)
1520 if (context.request_id && !context.is_pepper_menu) {
1521 ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins(
1522 source_web_contents_, false, std::string());
1524 #endif
1525 RenderFrameHost* render_frame_host =
1526 RenderFrameHost::FromID(render_process_id_, render_frame_id_);
1527 if (render_frame_host)
1528 render_frame_host->ExecuteCustomContextMenuCommand(action, context);
1529 return;
1532 // Process extension menu items.
1533 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
1534 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
1535 extension_items_.ExecuteCommand(id, source_web_contents_, params_);
1536 return;
1539 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
1540 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
1541 ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1542 GetHandlersForLinkUrl();
1543 if (handlers.empty()) {
1544 return;
1546 content::RecordAction(
1547 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Open"));
1548 int handlerIndex = id - IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
1549 WindowOpenDisposition disposition =
1550 ForceNewTabDispositionFromEventFlags(event_flags);
1551 OpenURL(
1552 handlers[handlerIndex].TranslateUrl(params_.link_url),
1553 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1554 params_.frame_id,
1555 disposition,
1556 content::PAGE_TRANSITION_LINK);
1557 return;
1560 switch (id) {
1561 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: {
1562 Browser* browser =
1563 chrome::FindBrowserWithWebContents(source_web_contents_);
1564 OpenURL(
1565 params_.link_url,
1566 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1567 params_.frame_id,
1568 !browser || browser->is_app() ?
1569 NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB,
1570 content::PAGE_TRANSITION_LINK);
1571 break;
1573 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
1574 OpenURL(
1575 params_.link_url,
1576 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1577 params_.frame_id,
1578 NEW_WINDOW, content::PAGE_TRANSITION_LINK);
1579 break;
1581 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
1582 OpenURL(params_.link_url,
1583 GURL(),
1584 params_.frame_id,
1585 OFF_THE_RECORD,
1586 content::PAGE_TRANSITION_LINK);
1587 break;
1589 case IDC_CONTENT_CONTEXT_SAVELINKAS: {
1590 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
1591 const GURL& referrer =
1592 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url;
1593 const GURL& url = params_.link_url;
1594 DownloadManager* dlm = BrowserContext::GetDownloadManager(profile_);
1595 scoped_ptr<DownloadUrlParameters> dl_params(
1596 DownloadUrlParameters::FromWebContents(source_web_contents_, url));
1597 dl_params->set_referrer(
1598 content::Referrer(referrer, params_.referrer_policy));
1599 dl_params->set_referrer_encoding(params_.frame_charset);
1600 dl_params->set_prompt(true);
1601 dlm->DownloadUrl(dl_params.Pass());
1602 break;
1605 case IDC_CONTENT_CONTEXT_SAVEAVAS:
1606 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: {
1607 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
1608 const GURL& referrer =
1609 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url;
1610 const GURL& url = params_.src_url;
1611 int64 post_id = -1;
1612 if (url == source_web_contents_->GetURL()) {
1613 const NavigationEntry* entry =
1614 source_web_contents_->GetController().GetActiveEntry();
1615 if (entry)
1616 post_id = entry->GetPostID();
1618 DownloadManager* dlm = BrowserContext::GetDownloadManager(profile_);
1619 scoped_ptr<DownloadUrlParameters> dl_params(
1620 DownloadUrlParameters::FromWebContents(source_web_contents_, url));
1621 dl_params->set_referrer(
1622 content::Referrer(referrer, params_.referrer_policy));
1623 dl_params->set_post_id(post_id);
1624 dl_params->set_prefer_cache(true);
1625 if (post_id >= 0)
1626 dl_params->set_method("POST");
1627 dl_params->set_prompt(true);
1628 dlm->DownloadUrl(dl_params.Pass());
1629 break;
1632 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION:
1633 WriteURLToClipboard(params_.unfiltered_link_url);
1634 break;
1636 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION:
1637 case IDC_CONTENT_CONTEXT_COPYAVLOCATION:
1638 WriteURLToClipboard(params_.src_url);
1639 break;
1641 case IDC_CONTENT_CONTEXT_COPYIMAGE:
1642 CopyImageAt(params_.x, params_.y);
1643 break;
1645 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
1646 GetImageThumbnailForSearch();
1647 break;
1649 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
1650 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
1651 OpenURL(
1652 params_.src_url,
1653 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1654 params_.frame_id,
1655 NEW_BACKGROUND_TAB, content::PAGE_TRANSITION_LINK);
1656 break;
1658 case IDC_CONTENT_CONTEXT_PLAYPAUSE: {
1659 bool play = !!(params_.media_flags & WebContextMenuData::MediaPaused);
1660 if (play) {
1661 content::RecordAction(UserMetricsAction("MediaContextMenu_Play"));
1662 } else {
1663 content::RecordAction(UserMetricsAction("MediaContextMenu_Pause"));
1665 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1666 WebMediaPlayerAction(
1667 WebMediaPlayerAction::Play, play));
1668 break;
1671 case IDC_CONTENT_CONTEXT_MUTE: {
1672 bool mute = !(params_.media_flags & WebContextMenuData::MediaMuted);
1673 if (mute) {
1674 content::RecordAction(UserMetricsAction("MediaContextMenu_Mute"));
1675 } else {
1676 content::RecordAction(UserMetricsAction("MediaContextMenu_Unmute"));
1678 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1679 WebMediaPlayerAction(
1680 WebMediaPlayerAction::Mute, mute));
1681 break;
1684 case IDC_CONTENT_CONTEXT_LOOP:
1685 content::RecordAction(UserMetricsAction("MediaContextMenu_Loop"));
1686 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1687 WebMediaPlayerAction(
1688 WebMediaPlayerAction::Loop,
1689 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP)));
1690 break;
1692 case IDC_CONTENT_CONTEXT_CONTROLS:
1693 content::RecordAction(UserMetricsAction("MediaContextMenu_Controls"));
1694 MediaPlayerActionAt(
1695 gfx::Point(params_.x, params_.y),
1696 WebMediaPlayerAction(
1697 WebMediaPlayerAction::Controls,
1698 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS)));
1699 break;
1701 case IDC_CONTENT_CONTEXT_ROTATECW:
1702 content::RecordAction(
1703 UserMetricsAction("PluginContextMenu_RotateClockwise"));
1704 PluginActionAt(
1705 gfx::Point(params_.x, params_.y),
1706 WebPluginAction(
1707 WebPluginAction::Rotate90Clockwise,
1708 true));
1709 break;
1711 case IDC_CONTENT_CONTEXT_ROTATECCW:
1712 content::RecordAction(
1713 UserMetricsAction("PluginContextMenu_RotateCounterclockwise"));
1714 PluginActionAt(
1715 gfx::Point(params_.x, params_.y),
1716 WebPluginAction(
1717 WebPluginAction::Rotate90Counterclockwise,
1718 true));
1719 break;
1721 case IDC_BACK:
1722 source_web_contents_->GetController().GoBack();
1723 break;
1725 case IDC_FORWARD:
1726 source_web_contents_->GetController().GoForward();
1727 break;
1729 case IDC_SAVE_PAGE:
1730 source_web_contents_->OnSavePage();
1731 break;
1733 case IDC_RELOAD:
1734 source_web_contents_->GetController().Reload(true);
1735 break;
1737 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP: {
1738 const Extension* platform_app = GetExtension();
1739 DCHECK(platform_app);
1740 DCHECK(platform_app->is_platform_app());
1742 extensions::ExtensionSystem::Get(profile_)->extension_service()->
1743 ReloadExtension(platform_app->id());
1744 break;
1747 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP: {
1748 const Extension* platform_app = GetExtension();
1749 DCHECK(platform_app);
1750 DCHECK(platform_app->is_platform_app());
1752 apps::AppLoadService::Get(profile_)->RestartApplication(
1753 platform_app->id());
1754 break;
1757 case IDC_PRINT:
1758 #if defined(ENABLE_PRINTING)
1759 if (params_.media_type == WebContextMenuData::MediaTypeNone) {
1760 #if defined(ENABLE_FULL_PRINTING)
1761 printing::PrintViewManager* print_view_manager =
1762 printing::PrintViewManager::FromWebContents(source_web_contents_);
1764 if (!print_view_manager)
1765 break;
1766 if (profile_->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled)) {
1767 print_view_manager->PrintNow();
1768 } else {
1769 print_view_manager->PrintPreviewNow(!params_.selection_text.empty());
1771 #else
1772 printing::PrintViewManagerBasic* print_view_manager =
1773 printing::PrintViewManagerBasic::FromWebContents(
1774 source_web_contents_);
1775 if (!print_view_manager)
1776 break;
1777 print_view_manager->PrintNow();
1778 #endif // defined(ENABLE_FULL_PRINTING)
1779 } else {
1780 rvh->Send(new PrintMsg_PrintNodeUnderContextMenu(rvh->GetRoutingID()));
1782 #endif // defined(ENABLE_PRINTING)
1783 break;
1785 case IDC_VIEW_SOURCE:
1786 source_web_contents_->ViewSource();
1787 break;
1789 case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
1790 Inspect(params_.x, params_.y);
1791 break;
1793 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE: {
1794 const Extension* platform_app = GetExtension();
1795 DCHECK(platform_app);
1796 DCHECK(platform_app->is_platform_app());
1798 extensions::devtools_util::InspectBackgroundPage(platform_app, profile_);
1799 break;
1802 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: {
1803 NavigationController* controller = &source_web_contents_->GetController();
1804 // Important to use GetVisibleEntry to match what's showing in the
1805 // omnibox.
1806 NavigationEntry* nav_entry = controller->GetVisibleEntry();
1807 Browser* browser =
1808 chrome::FindBrowserWithWebContents(source_web_contents_);
1809 chrome::ShowWebsiteSettings(browser, source_web_contents_,
1810 nav_entry->GetURL(), nav_entry->GetSSL());
1811 break;
1814 case IDC_CONTENT_CONTEXT_TRANSLATE: {
1815 // A translation might have been triggered by the time the menu got
1816 // selected, do nothing in that case.
1817 TranslateTabHelper* translate_tab_helper =
1818 TranslateTabHelper::FromWebContents(source_web_contents_);
1819 if (!translate_tab_helper ||
1820 translate_tab_helper->GetLanguageState().IsPageTranslated() ||
1821 translate_tab_helper->GetLanguageState().translation_pending()) {
1822 return;
1824 std::string original_lang =
1825 translate_tab_helper->GetLanguageState().original_language();
1826 std::string target_lang = g_browser_process->GetApplicationLocale();
1827 target_lang = TranslateDownloadManager::GetLanguageCode(target_lang);
1828 // Since the user decided to translate for that language and site, clears
1829 // any preferences for not translating them.
1830 scoped_ptr<TranslatePrefs> prefs(
1831 TranslateTabHelper::CreateTranslatePrefs(profile_->GetPrefs()));
1832 prefs->UnblockLanguage(original_lang);
1833 prefs->RemoveSiteFromBlacklist(params_.page_url.HostNoBrackets());
1834 TranslateManager::GetInstance()->TranslatePage(
1835 source_web_contents_, original_lang, target_lang);
1836 break;
1839 case IDC_CONTENT_CONTEXT_RELOADFRAME:
1840 rvh->ReloadFrame();
1841 break;
1843 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
1844 source_web_contents_->ViewFrameSource(params_.frame_url,
1845 params_.frame_page_state);
1846 break;
1848 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO: {
1849 Browser* browser = chrome::FindBrowserWithWebContents(
1850 source_web_contents_);
1851 chrome::ShowWebsiteSettings(browser, source_web_contents_,
1852 params_.frame_url, params_.security_info);
1853 break;
1856 case IDC_CONTENT_CONTEXT_UNDO:
1857 rvh->Undo();
1858 break;
1860 case IDC_CONTENT_CONTEXT_REDO:
1861 rvh->Redo();
1862 break;
1864 case IDC_CONTENT_CONTEXT_CUT:
1865 rvh->Cut();
1866 break;
1868 case IDC_CONTENT_CONTEXT_COPY:
1869 rvh->Copy();
1870 break;
1872 case IDC_CONTENT_CONTEXT_PASTE:
1873 rvh->Paste();
1874 break;
1876 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE:
1877 rvh->PasteAndMatchStyle();
1878 break;
1880 case IDC_CONTENT_CONTEXT_DELETE:
1881 rvh->Delete();
1882 break;
1884 case IDC_CONTENT_CONTEXT_SELECTALL:
1885 rvh->SelectAll();
1886 break;
1888 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR:
1889 case IDC_CONTENT_CONTEXT_GOTOURL: {
1890 WindowOpenDisposition disposition =
1891 ForceNewTabDispositionFromEventFlags(event_flags);
1892 OpenURL(selection_navigation_url_,
1893 GURL(),
1894 params_.frame_id,
1895 disposition,
1896 content::PAGE_TRANSITION_LINK);
1897 break;
1899 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS: {
1900 WindowOpenDisposition disposition =
1901 ForceNewTabDispositionFromEventFlags(event_flags);
1902 std::string url = std::string(chrome::kChromeUISettingsURL) +
1903 chrome::kLanguageOptionsSubPage;
1904 OpenURL(GURL(url), GURL(), 0, disposition, content::PAGE_TRANSITION_LINK);
1905 break;
1908 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS: {
1909 content::RecordAction(
1910 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Settings"));
1911 WindowOpenDisposition disposition =
1912 ForceNewTabDispositionFromEventFlags(event_flags);
1913 std::string url = std::string(chrome::kChromeUISettingsURL) +
1914 chrome::kHandlerSettingsSubPage;
1915 OpenURL(GURL(url), GURL(), 0, disposition, content::PAGE_TRANSITION_LINK);
1916 break;
1919 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE: {
1920 // Make sure the model is loaded.
1921 TemplateURLService* model =
1922 TemplateURLServiceFactory::GetForProfile(profile_);
1923 if (!model)
1924 return;
1925 model->Load();
1927 SearchEngineTabHelper* search_engine_tab_helper =
1928 SearchEngineTabHelper::FromWebContents(source_web_contents_);
1929 if (search_engine_tab_helper &&
1930 search_engine_tab_helper->delegate()) {
1931 base::string16 keyword(
1932 TemplateURLService::GenerateKeyword(params_.page_url));
1933 TemplateURLData data;
1934 data.short_name = keyword;
1935 data.SetKeyword(keyword);
1936 data.SetURL(params_.keyword_url.spec());
1937 data.favicon_url =
1938 TemplateURL::GenerateFaviconURL(params_.page_url.GetOrigin());
1939 // Takes ownership of the TemplateURL.
1940 search_engine_tab_helper->delegate()->
1941 ConfirmAddSearchProvider(new TemplateURL(profile_, data), profile_);
1943 break;
1946 #if defined(ENABLE_INPUT_SPEECH)
1947 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES: {
1948 profile_->GetPrefs()->SetBoolean(
1949 prefs::kSpeechRecognitionFilterProfanities,
1950 !profile_->GetPrefs()->GetBoolean(
1951 prefs::kSpeechRecognitionFilterProfanities));
1952 break;
1954 #endif
1955 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT: {
1956 GURL url(chrome::kSpeechInputAboutURL);
1957 GURL localized_url = google_util::AppendGoogleLocaleParam(url);
1958 // Open URL with no referrer field (because user clicked on menu item).
1959 OpenURL(localized_url, GURL(), 0, NEW_FOREGROUND_TAB,
1960 content::PAGE_TRANSITION_LINK);
1961 break;
1964 default:
1965 NOTREACHED();
1966 break;
1970 ProtocolHandlerRegistry::ProtocolHandlerList
1971 RenderViewContextMenu::GetHandlersForLinkUrl() {
1972 ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1973 protocol_handler_registry_->GetHandlersFor(params_.link_url.scheme());
1974 std::sort(handlers.begin(), handlers.end());
1975 return handlers;
1978 void RenderViewContextMenu::MenuWillShow(ui::SimpleMenuModel* source) {
1979 for (int i = 0; i < source->GetItemCount(); ++i) {
1980 if (source->IsVisibleAt(i) &&
1981 source->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR) {
1982 RecordShownItem(source->GetCommandIdAt(i));
1986 // Ignore notifications from submenus.
1987 if (source != &menu_model_)
1988 return;
1990 content::RenderWidgetHostView* view =
1991 source_web_contents_->GetRenderWidgetHostView();
1992 if (view)
1993 view->SetShowingContextMenu(true);
1995 content::NotificationService::current()->Notify(
1996 chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN,
1997 content::Source<RenderViewContextMenu>(this),
1998 content::NotificationService::NoDetails());
2001 void RenderViewContextMenu::MenuClosed(ui::SimpleMenuModel* source) {
2002 // Ignore notifications from submenus.
2003 if (source != &menu_model_)
2004 return;
2006 content::RenderWidgetHostView* view =
2007 source_web_contents_->GetRenderWidgetHostView();
2008 if (view)
2009 view->SetShowingContextMenu(false);
2010 RenderFrameHost* render_frame_host =
2011 RenderFrameHost::FromID(render_process_id_, render_frame_id_);
2012 if (render_frame_host)
2013 render_frame_host->NotifyContextMenuClosed(params_.custom_context);
2015 if (!command_executed_) {
2016 FOR_EACH_OBSERVER(RenderViewContextMenuObserver,
2017 observers_,
2018 OnMenuCancel());
2022 bool RenderViewContextMenu::IsDevCommandEnabled(int id) const {
2023 if (id == IDC_CONTENT_CONTEXT_INSPECTELEMENT ||
2024 id == IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE) {
2025 const CommandLine* command_line = CommandLine::ForCurrentProcess();
2026 if (!profile_->GetPrefs()->GetBoolean(prefs::kWebKitJavascriptEnabled) ||
2027 command_line->HasSwitch(switches::kDisableJavaScript))
2028 return false;
2030 // Don't enable the web inspector if the developer tools are disabled via
2031 // the preference dev-tools-disabled.
2032 if (profile_->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled))
2033 return false;
2036 return true;
2039 base::string16 RenderViewContextMenu::PrintableSelectionText() {
2040 return gfx::TruncateString(params_.selection_text,
2041 kMaxSelectionTextLength);
2044 // Controller functions --------------------------------------------------------
2046 void RenderViewContextMenu::OpenURL(
2047 const GURL& url, const GURL& referrer, int64 frame_id,
2048 WindowOpenDisposition disposition,
2049 content::PageTransition transition) {
2050 // Ensure that URL fragment, username and password fields are not sent
2051 // in the referrer.
2052 GURL sanitized_referrer(referrer);
2053 if (sanitized_referrer.is_valid() && (sanitized_referrer.has_ref() ||
2054 sanitized_referrer.has_username() || sanitized_referrer.has_password())) {
2055 GURL::Replacements referrer_mods;
2056 referrer_mods.ClearRef();
2057 referrer_mods.ClearUsername();
2058 referrer_mods.ClearPassword();
2059 sanitized_referrer = sanitized_referrer.ReplaceComponents(referrer_mods);
2062 WebContents* new_contents = source_web_contents_->OpenURL(OpenURLParams(
2063 url, content::Referrer(sanitized_referrer, params_.referrer_policy),
2064 disposition, transition, false));
2065 if (!new_contents)
2066 return;
2068 RetargetingDetails details;
2069 details.source_web_contents = source_web_contents_;
2070 details.source_frame_id = frame_id;
2071 details.target_url = url;
2072 details.target_web_contents = new_contents;
2073 details.not_yet_in_tabstrip = false;
2074 content::NotificationService::current()->Notify(
2075 chrome::NOTIFICATION_RETARGETING,
2076 content::Source<Profile>(Profile::FromBrowserContext(
2077 source_web_contents_->GetBrowserContext())),
2078 content::Details<RetargetingDetails>(&details));
2081 void RenderViewContextMenu::CopyImageAt(int x, int y) {
2082 source_web_contents_->GetRenderViewHost()->CopyImageAt(x, y);
2085 void RenderViewContextMenu::GetImageThumbnailForSearch() {
2086 source_web_contents_->GetRenderViewHost()->Send(
2087 new ChromeViewMsg_RequestThumbnailForContextNode(
2088 source_web_contents_->GetRenderViewHost()->GetRoutingID(),
2089 kImageSearchThumbnailMinSize,
2090 gfx::Size(kImageSearchThumbnailMaxWidth,
2091 kImageSearchThumbnailMaxHeight)));
2094 void RenderViewContextMenu::Inspect(int x, int y) {
2095 content::RecordAction(UserMetricsAction("DevTools_InspectElement"));
2096 source_web_contents_->GetRenderViewHostAtPosition(
2097 x, y, base::Bind(&DevToolsInspectElementAt));
2100 void RenderViewContextMenu::WriteURLToClipboard(const GURL& url) {
2101 chrome_common_net::WriteURLToClipboard(
2102 url,
2103 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages),
2104 ui::Clipboard::GetForCurrentThread());
2107 void RenderViewContextMenu::MediaPlayerActionAt(
2108 const gfx::Point& location,
2109 const WebMediaPlayerAction& action) {
2110 source_web_contents_->GetRenderViewHost()->
2111 ExecuteMediaPlayerActionAtLocation(location, action);
2114 void RenderViewContextMenu::PluginActionAt(
2115 const gfx::Point& location,
2116 const WebPluginAction& action) {
2117 source_web_contents_->GetRenderViewHost()->
2118 ExecutePluginActionAtLocation(location, action);