- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / tab_contents / render_view_context_menu.cc
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.
4
5 #include "chrome/browser/tab_contents/render_view_context_menu.h"
6
7 #include <algorithm>
8 #include <set>
9 #include <utility>
10
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/extensions/extension.h"
65 #include "chrome/common/net/url_util.h"
66 #include "chrome/common/pref_names.h"
67 #include "chrome/common/render_messages.h"
68 #include "chrome/common/spellcheck_messages.h"
69 #include "chrome/common/url_constants.h"
70 #include "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_process_host.h"
78 #include "content/public/browser/render_view_host.h"
79 #include "content/public/browser/render_widget_host_view.h"
80 #include "content/public/browser/user_metrics.h"
81 #include "content/public/browser/web_contents.h"
82 #include "content/public/common/menu_item.h"
83 #include "content/public/common/ssl_status.h"
84 #include "content/public/common/url_utils.h"
85 #include "extensions/browser/view_type_utils.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"
97
98 #if defined(ENABLE_PRINTING)
99 #include "chrome/common/print_messages.h"
100
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)
109
110 using WebKit::WebContextMenuData;
111 using WebKit::WebMediaPlayerAction;
112 using WebKit::WebPluginAction;
113 using WebKit::WebString;
114 using WebKit::WebURL;
115 using content::BrowserContext;
116 using content::ChildProcessSecurityPolicy;
117 using content::DownloadManager;
118 using content::DownloadUrlParameters;
119 using content::NavigationController;
120 using content::NavigationEntry;
121 using content::OpenURLParams;
122 using content::RenderViewHost;
123 using content::SSLStatus;
124 using content::UserMetricsAction;
125 using content::WebContents;
126 using extensions::Extension;
127 using extensions::MenuItem;
128 using extensions::MenuManager;
129
130 namespace {
131
132 const int kImageSearchThumbnailMinSize = 300 * 300;
133 const int kImageSearchThumbnailMaxWidth = 600;
134 const int kImageSearchThumbnailMaxHeight = 600;
135
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.
205 };
206
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;
212   }
213
214   if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
215       id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
216     return IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST;
217   }
218
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;
222   }
223
224   if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
225       id <= IDC_SPELLCHECK_LANGUAGES_LAST) {
226     return IDC_SPELLCHECK_LANGUAGES_FIRST;
227   }
228
229   return id;
230 }
231
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;
239     }
240   }
241   return -1;
242 }
243
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;
253   }
254 }
255
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;
267   }
268 }
269
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;
278 }
279
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;
290     }
291   }
292   return false;
293 }
294
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;
305     }
306   }
307   return false;
308 }
309
310 const size_t kMaxCustomMenuDepth = 5;
311 const size_t kMaxCustomMenuTotalItems = 1000;
312
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;
321   }
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;
327     }
328     if (*total_items >= kMaxCustomMenuTotalItems) {
329       LOG(ERROR) << "Custom menu too large (too many items).";
330       return;
331     }
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;
360       }
361       default:
362         NOTREACHED();
363         break;
364     }
365   }
366 }
367
368 void DevToolsInspectElementAt(RenderViewHost* rvh, int x, int y) {
369   DevToolsWindow::InspectElement(rvh, x, y);
370 }
371
372 // Helper function to escape "&" as "&&".
373 void EscapeAmpersands(string16* text) {
374   const char16 ampersand[] = {'&', 0};
375   ReplaceChars(*text, ampersand, ASCIIToUTF16("&&"), text);
376 }
377
378 }  // namespace
379
380 // static
381 const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50;
382
383 // static
384 bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) {
385   return url.SchemeIs(chrome::kChromeDevToolsScheme);
386 }
387
388 // static
389 bool RenderViewContextMenu::IsInternalResourcesURL(const GURL& url) {
390   if (!url.SchemeIs(chrome::kChromeUIScheme))
391     return false;
392   return url.host() == chrome::kChromeUISyncResourcesHost;
393 }
394
395 static const int kSpellcheckRadioGroup = 1;
396
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       external_(false),
407       speech_input_submenu_model_(this),
408       protocol_handler_submenu_model_(this),
409       protocol_handler_registry_(
410           ProtocolHandlerRegistryFactory::GetForProfile(profile_)),
411       command_executed_(false),
412       is_guest_(false) {
413   RenderViewHost* rvh = source_web_contents_->GetRenderViewHost();
414   if (rvh && rvh->GetProcess()->IsGuest())
415     is_guest_ = true;
416 }
417
418 RenderViewContextMenu::~RenderViewContextMenu() {
419 }
420
421 // Menu construction functions -------------------------------------------------
422
423 void RenderViewContextMenu::Init() {
424   InitMenu();
425   PlatformInit();
426 }
427
428 void RenderViewContextMenu::Cancel() {
429   PlatformCancel();
430 }
431
432 static bool ExtensionPatternMatch(const extensions::URLPatternSet& patterns,
433                                   const GURL& url) {
434   // No patterns means no restriction, so that implicitly matches.
435   if (patterns.is_empty())
436     return true;
437   return patterns.MatchesURL(url);
438 }
439
440 // static
441 bool RenderViewContextMenu::ExtensionContextAndPatternMatch(
442     const content::ContextMenuParams& params,
443     MenuItem::ContextList contexts,
444     const extensions::URLPatternSet& target_url_patterns) {
445   const bool has_link = !params.link_url.is_empty();
446   const bool has_selection = !params.selection_text.empty();
447   const bool in_frame = !params.frame_url.is_empty();
448
449   if (contexts.Contains(MenuItem::ALL) ||
450       (has_selection && contexts.Contains(MenuItem::SELECTION)) ||
451       (params.is_editable && contexts.Contains(MenuItem::EDITABLE)) ||
452       (in_frame && contexts.Contains(MenuItem::FRAME)))
453     return true;
454
455   if (has_link && contexts.Contains(MenuItem::LINK) &&
456       ExtensionPatternMatch(target_url_patterns, params.link_url))
457     return true;
458
459   switch (params.media_type) {
460     case WebContextMenuData::MediaTypeImage:
461       if (contexts.Contains(MenuItem::IMAGE) &&
462           ExtensionPatternMatch(target_url_patterns, params.src_url))
463         return true;
464       break;
465
466     case WebContextMenuData::MediaTypeVideo:
467       if (contexts.Contains(MenuItem::VIDEO) &&
468           ExtensionPatternMatch(target_url_patterns, params.src_url))
469         return true;
470       break;
471
472     case WebContextMenuData::MediaTypeAudio:
473       if (contexts.Contains(MenuItem::AUDIO) &&
474           ExtensionPatternMatch(target_url_patterns, params.src_url))
475         return true;
476       break;
477
478     default:
479       break;
480   }
481
482   // PAGE is the least specific context, so we only examine that if none of the
483   // other contexts apply (except for FRAME, which is included in PAGE for
484   // backwards compatibility).
485   if (!has_link && !has_selection && !params.is_editable &&
486       params.media_type == WebContextMenuData::MediaTypeNone &&
487       contexts.Contains(MenuItem::PAGE))
488     return true;
489
490   return false;
491 }
492
493 static const GURL& GetDocumentURL(const content::ContextMenuParams& params) {
494   return params.frame_url.is_empty() ? params.page_url : params.frame_url;
495 }
496
497 // static
498 bool RenderViewContextMenu::MenuItemMatchesParams(
499     const content::ContextMenuParams& params,
500     const extensions::MenuItem* item) {
501   bool match = ExtensionContextAndPatternMatch(params, item->contexts(),
502                                                item->target_url_patterns());
503   if (!match)
504     return false;
505
506   const GURL& document_url = GetDocumentURL(params);
507   return ExtensionPatternMatch(item->document_url_patterns(), document_url);
508 }
509
510 void RenderViewContextMenu::AppendAllExtensionItems() {
511   extension_items_.Clear();
512   ExtensionService* service =
513       extensions::ExtensionSystem::Get(profile_)->extension_service();
514   if (!service)
515     return;  // In unit-tests, we may not have an ExtensionService.
516   MenuManager* menu_manager = service->menu_manager();
517
518   string16 printable_selection_text = PrintableSelectionText();
519   EscapeAmpersands(&printable_selection_text);
520
521   // Get a list of extension id's that have context menu items, and sort by the
522   // top level context menu title of the extension.
523   std::set<std::string> ids = menu_manager->ExtensionIds();
524   std::vector<base::string16> sorted_menu_titles;
525   std::map<base::string16, std::string> map_ids;
526   for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) {
527     const Extension* extension = service->GetExtensionById(*i, false);
528     // Platform apps have their context menus created directly in
529     // AppendPlatformAppItems.
530     if (extension && !extension->is_platform_app()) {
531       base::string16 menu_title = extension_items_.GetTopLevelContextMenuTitle(
532           *i, printable_selection_text);
533       map_ids[menu_title] = *i;
534       sorted_menu_titles.push_back(menu_title);
535     }
536   }
537   if (sorted_menu_titles.empty())
538     return;
539
540   const std::string app_locale = g_browser_process->GetApplicationLocale();
541   l10n_util::SortStrings16(app_locale, &sorted_menu_titles);
542
543   int index = 0;
544   base::TimeTicks begin = base::TimeTicks::Now();
545   for (size_t i = 0; i < sorted_menu_titles.size(); ++i) {
546     const std::string& id = map_ids[sorted_menu_titles[i]];
547     extension_items_.AppendExtensionItems(id, printable_selection_text,
548                                           &index);
549   }
550
551   UMA_HISTOGRAM_TIMES("Extensions.ContextMenus_BuildTime",
552                       base::TimeTicks::Now() - begin);
553   UMA_HISTOGRAM_COUNTS("Extensions.ContextMenus_ItemCount", index);
554 }
555
556 void RenderViewContextMenu::InitMenu() {
557   if (chrome::IsRunningInForcedAppMode()) {
558     AppendAppModeItems();
559     return;
560   }
561
562   extensions::ViewType view_type =
563       extensions::GetViewType(source_web_contents_);
564   if (view_type == extensions::VIEW_TYPE_APP_SHELL) {
565     AppendPlatformAppItems();
566     return;
567   } else if (view_type == extensions::VIEW_TYPE_EXTENSION_POPUP) {
568     AppendPopupExtensionItems();
569     return;
570   } else if (view_type == extensions::VIEW_TYPE_PANEL) {
571     AppendPanelItems();
572     return;
573   }
574
575   const bool has_link = !params_.unfiltered_link_url.is_empty();
576   const bool has_selection = !params_.selection_text.empty();
577
578   if (AppendCustomItems()) {
579     // If there's a selection, don't early return when there are custom items,
580     // but fall through to adding the normal ones after the custom ones.
581     if (has_selection) {
582       menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
583     } else {
584       // Don't add items for Pepper menu.
585       if (!params_.custom_context.is_pepper_menu)
586         AppendDeveloperItems();
587       return;
588     }
589   }
590
591   // When no special node or text is selected and selection has no link,
592   // show page items.
593   if (params_.media_type == WebContextMenuData::MediaTypeNone &&
594       !has_link &&
595       !params_.is_editable &&
596       !is_guest_ &&
597       !has_selection) {
598     if (!params_.page_url.is_empty()) {
599       bool is_devtools = IsDevToolsURL(params_.page_url);
600       if (!is_devtools && !IsInternalResourcesURL(params_.page_url)) {
601         AppendPageItems();
602         // Merge in frame items if we clicked within a frame that needs them.
603         if (!params_.frame_url.is_empty()) {
604           is_devtools = IsDevToolsURL(params_.frame_url);
605           if (!is_devtools && !IsInternalResourcesURL(params_.frame_url)) {
606             menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
607             AppendFrameItems();
608           }
609         }
610       }
611     } else {
612       DCHECK(params_.frame_url.is_empty());
613     }
614   }
615
616   // Do not show link related items for guest.
617   if (has_link && !is_guest_) {
618     AppendLinkItems();
619     if (params_.media_type != WebContextMenuData::MediaTypeNone)
620       menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
621   }
622
623   switch (params_.media_type) {
624     case WebContextMenuData::MediaTypeNone:
625       break;
626     case WebContextMenuData::MediaTypeImage:
627       AppendImageItems();
628       break;
629     case WebContextMenuData::MediaTypeVideo:
630       AppendVideoItems();
631       break;
632     case WebContextMenuData::MediaTypeAudio:
633       AppendAudioItems();
634       break;
635     case WebContextMenuData::MediaTypePlugin:
636       AppendPluginItems();
637       break;
638 #ifdef WEBCONTEXT_MEDIATYPEFILE_DEFINED
639     case WebContextMenuData::MediaTypeFile:
640       break;
641 #endif
642   }
643
644   if (params_.is_editable)
645     AppendEditableItems();
646   else if (has_selection)
647     AppendCopyItem();
648
649   if (!is_guest_ && has_selection) {
650     AppendSearchProvider();
651     if (!IsDevToolsURL(params_.page_url))
652       AppendPrintItem();
653   }
654
655   if (!IsDevToolsURL(params_.page_url) && !is_guest_)
656     AppendAllExtensionItems();
657
658   AppendDeveloperItems();
659
660   if (!is_guest_) {
661 #if defined(ENABLE_FULL_PRINTING)
662     if (!print_preview_menu_observer_.get()) {
663       print_preview_menu_observer_.reset(
664           new PrintPreviewContextMenuObserver(source_web_contents_));
665     }
666
667     observers_.AddObserver(print_preview_menu_observer_.get());
668 #endif
669   }
670 }
671
672 const Extension* RenderViewContextMenu::GetExtension() const {
673   extensions::ExtensionSystem* system =
674       extensions::ExtensionSystem::Get(profile_);
675   // There is no process manager in some tests.
676   if (!system->process_manager())
677     return NULL;
678
679   return system->process_manager()->GetExtensionForRenderViewHost(
680       source_web_contents_->GetRenderViewHost());
681 }
682
683 void RenderViewContextMenu::AppendAppModeItems() {
684   const bool has_selection = !params_.selection_text.empty();
685
686   if (params_.is_editable)
687     AppendEditableItems();
688   else if (has_selection)
689     AppendCopyItem();
690 }
691
692 void RenderViewContextMenu::AppendPlatformAppItems() {
693   const Extension* platform_app = GetExtension();
694
695   // The RVH might be for a process sandboxed from the extension.
696   if (!platform_app)
697     return;
698
699   DCHECK(platform_app->is_platform_app());
700
701   const bool has_selection = !params_.selection_text.empty();
702
703   // Add undo/redo, cut/copy/paste etc for text fields.
704   if (params_.is_editable)
705     AppendEditableItems();
706   else if (has_selection)
707     AppendCopyItem();
708
709   int index = 0;
710   extension_items_.AppendExtensionItems(platform_app->id(),
711                                         PrintableSelectionText(), &index);
712
713   // Add dev tools for unpacked extensions.
714   if (extensions::Manifest::IsUnpackedLocation(platform_app->location()) ||
715       CommandLine::ForCurrentProcess()->HasSwitch(
716           switches::kDebugPackedApps)) {
717     // Add a separator if there are any items already in the menu.
718     menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
719
720     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP,
721                                     IDS_CONTENT_CONTEXT_RELOAD_PACKAGED_APP);
722     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP,
723                                     IDS_CONTENT_CONTEXT_RESTART_APP);
724     AppendDeveloperItems();
725     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE,
726                                     IDS_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE);
727   }
728 }
729
730 void RenderViewContextMenu::AppendPopupExtensionItems() {
731   const bool has_selection = !params_.selection_text.empty();
732
733   if (params_.is_editable)
734     AppendEditableItems();
735   else if (has_selection)
736     AppendCopyItem();
737
738   if (has_selection)
739     AppendSearchProvider();
740
741   AppendAllExtensionItems();
742   AppendDeveloperItems();
743 }
744
745 void RenderViewContextMenu::AppendPanelItems() {
746   bool has_selection = !params_.selection_text.empty();
747
748   // Checking link should take precedence before checking selection since on Mac
749   // right-clicking a link will also make it selected.
750   if (params_.unfiltered_link_url.is_valid())
751     AppendLinkItems();
752
753   if (params_.is_editable)
754     AppendEditableItems();
755   else if (has_selection)
756     AppendCopyItem();
757
758   // Avoid appending extension related items when |extension| is null. This
759   // happens when the panel is navigated to a url outside of the extension's
760   // package.
761   const Extension* extension = GetExtension();
762   if (extension) {
763     // Only add extension items from this extension.
764     int index = 0;
765     extension_items_.AppendExtensionItems(extension->id(),
766                                           PrintableSelectionText(), &index);
767   }
768 }
769
770 void RenderViewContextMenu::AddMenuItem(int command_id,
771                                         const string16& title) {
772   menu_model_.AddItem(command_id, title);
773 }
774
775 void RenderViewContextMenu::AddCheckItem(int command_id,
776                                          const string16& title) {
777   menu_model_.AddCheckItem(command_id, title);
778 }
779
780 void RenderViewContextMenu::AddSeparator() {
781   menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
782 }
783
784 void RenderViewContextMenu::AddSubMenu(int command_id,
785                                        const string16& label,
786                                        ui::MenuModel* model) {
787   menu_model_.AddSubMenu(command_id, label, model);
788 }
789
790 void RenderViewContextMenu::UpdateMenuItem(int command_id,
791                                            bool enabled,
792                                            bool hidden,
793                                            const string16& label) {
794   // This function needs platform-specific implementation.
795   NOTIMPLEMENTED();
796 }
797
798 RenderViewHost* RenderViewContextMenu::GetRenderViewHost() const {
799   return source_web_contents_->GetRenderViewHost();
800 }
801
802 WebContents* RenderViewContextMenu::GetWebContents() const {
803   return source_web_contents_;
804 }
805
806 Profile* RenderViewContextMenu::GetProfile() const {
807   return profile_;
808 }
809
810 bool RenderViewContextMenu::AppendCustomItems() {
811   size_t total_items = 0;
812   AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this,
813                        &menu_model_);
814   return total_items > 0;
815 }
816
817 void RenderViewContextMenu::AppendDeveloperItems() {
818   // Show Inspect Element in DevTools itself only in case of the debug
819   // devtools build.
820   bool show_developer_items = !IsDevToolsURL(params_.page_url);
821
822 #if defined(DEBUG_DEVTOOLS)
823   show_developer_items = true;
824 #endif
825
826   if (!show_developer_items)
827     return;
828
829   // In the DevTools popup menu, "developer items" is normally the only
830   // section, so omit the separator there.
831   menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
832   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTELEMENT,
833                                   IDS_CONTENT_CONTEXT_INSPECTELEMENT);
834 }
835
836 void RenderViewContextMenu::AppendLinkItems() {
837   if (!params_.link_url.is_empty()) {
838     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB,
839                                     IDS_CONTENT_CONTEXT_OPENLINKNEWTAB);
840     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW,
841                                     IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW);
842     if (params_.link_url.is_valid()) {
843       AppendProtocolHandlerSubMenu();
844     }
845
846     if (!external_) {
847       menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD,
848                                       IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD);
849     }
850     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVELINKAS,
851                                     IDS_CONTENT_CONTEXT_SAVELINKAS);
852   }
853
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);
859 }
860
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()));
880   }
881   AppendPrintItem();
882 }
883
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);
893 }
894
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);
904 }
905
906 void RenderViewContextMenu::AppendMediaItems() {
907   int media_flags = params_.media_flags;
908
909   menu_model_.AddItemWithStringId(
910       IDC_CONTENT_CONTEXT_PLAYPAUSE,
911       media_flags & WebContextMenuData::MediaPaused ?
912           IDS_CONTENT_CONTEXT_PLAY :
913           IDS_CONTENT_CONTEXT_PAUSE);
914
915   menu_model_.AddItemWithStringId(
916       IDC_CONTENT_CONTEXT_MUTE,
917       media_flags & WebContextMenuData::MediaMuted ?
918           IDS_CONTENT_CONTEXT_UNMUTE :
919           IDS_CONTENT_CONTEXT_MUTE);
920
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);
925 }
926
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);
936   }
937
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);
944   }
945 }
946
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);
955
956   if (TranslateManager::IsTranslatableURL(params_.page_url)) {
957     std::string locale = g_browser_process->GetApplicationLocale();
958     locale = TranslateManager::GetLanguageCode(locale);
959     string16 language = l10n_util::GetDisplayNameForLocale(locale, locale,
960                                                            true);
961     menu_model_.AddItem(
962         IDC_CONTENT_CONTEXT_TRANSLATE,
963         l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE, language));
964   }
965
966   menu_model_.AddItemWithStringId(IDC_VIEW_SOURCE,
967                                   IDS_CONTENT_CONTEXT_VIEWPAGESOURCE);
968   // Only add View Page Info if there's a browser.  This is a temporary thing
969   // while View Page Info crashes Chrome Frame; see http://crbug.com/120901.
970   // TODO(grt) Remove this once page info is back for Chrome Frame.
971   if (!external_) {
972     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWPAGEINFO,
973                                     IDS_CONTENT_CONTEXT_VIEWPAGEINFO);
974   }
975 }
976
977 void RenderViewContextMenu::AppendFrameItems() {
978   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOADFRAME,
979                                   IDS_CONTENT_CONTEXT_RELOADFRAME);
980   // These two menu items have yet to be implemented.
981   // http://code.google.com/p/chromium/issues/detail?id=11827
982   //   IDS_CONTENT_CONTEXT_SAVEFRAMEAS
983   //   IDS_CONTENT_CONTEXT_PRINTFRAME
984   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE,
985                                   IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE);
986   // Only add View Frame Info if there's a browser.  This is a temporary thing
987   // while View Frame Info crashes Chrome Frame; see http://crbug.com/120901.
988   // TODO(grt) Remove this once frame info is back for Chrome Frame.
989   if (!external_) {
990     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMEINFO,
991                                     IDS_CONTENT_CONTEXT_VIEWFRAMEINFO);
992   }
993 }
994
995 void RenderViewContextMenu::AppendCopyItem() {
996   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY,
997                                   IDS_CONTENT_CONTEXT_COPY);
998 }
999
1000 void RenderViewContextMenu::AppendPrintItem() {
1001   if (profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) &&
1002       (params_.media_type == WebContextMenuData::MediaTypeNone ||
1003        params_.media_flags & WebContextMenuData::MediaCanPrint)) {
1004     menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
1005   }
1006 }
1007
1008 void RenderViewContextMenu::AppendSearchProvider() {
1009   DCHECK(profile_);
1010
1011   TrimWhitespace(params_.selection_text, TRIM_ALL, &params_.selection_text);
1012   if (params_.selection_text.empty())
1013     return;
1014
1015   ReplaceChars(params_.selection_text, AutocompleteMatch::kInvalidChars,
1016                ASCIIToUTF16(" "), &params_.selection_text);
1017
1018   AutocompleteMatch match;
1019   AutocompleteClassifierFactory::GetForProfile(profile_)->Classify(
1020       params_.selection_text, false, false, &match, NULL);
1021   selection_navigation_url_ = match.destination_url;
1022   if (!selection_navigation_url_.is_valid())
1023     return;
1024
1025   string16 printable_selection_text = PrintableSelectionText();
1026   EscapeAmpersands(&printable_selection_text);
1027
1028   if (AutocompleteMatch::IsSearchType(match.type)) {
1029     const TemplateURL* const default_provider =
1030         TemplateURLServiceFactory::GetForProfile(profile_)->
1031         GetDefaultSearchProvider();
1032     if (!default_provider)
1033       return;
1034     menu_model_.AddItem(
1035         IDC_CONTENT_CONTEXT_SEARCHWEBFOR,
1036         l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR,
1037                                    default_provider->short_name(),
1038                                    printable_selection_text));
1039   } else {
1040     if ((selection_navigation_url_ != params_.link_url) &&
1041         ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1042             selection_navigation_url_.scheme())) {
1043       menu_model_.AddItem(
1044           IDC_CONTENT_CONTEXT_GOTOURL,
1045           l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_GOTOURL,
1046                                      printable_selection_text));
1047     }
1048   }
1049 }
1050
1051 void RenderViewContextMenu::AppendEditableItems() {
1052   const bool use_spellcheck_and_search = !chrome::IsRunningInForcedAppMode();
1053
1054   if (use_spellcheck_and_search)
1055     AppendSpellingSuggestionsSubMenu();
1056
1057   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO,
1058                                   IDS_CONTENT_CONTEXT_UNDO);
1059   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO,
1060                                   IDS_CONTENT_CONTEXT_REDO);
1061   menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1062   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_CUT,
1063                                   IDS_CONTENT_CONTEXT_CUT);
1064   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY,
1065                                   IDS_CONTENT_CONTEXT_COPY);
1066   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE,
1067                                   IDS_CONTENT_CONTEXT_PASTE);
1068   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE,
1069                                   IDS_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE);
1070   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_DELETE,
1071                                   IDS_CONTENT_CONTEXT_DELETE);
1072   menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1073
1074   if (use_spellcheck_and_search && !params_.keyword_url.is_empty()) {
1075     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ADDSEARCHENGINE,
1076                                     IDS_CONTENT_CONTEXT_ADDSEARCHENGINE);
1077     menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1078   }
1079
1080   if (use_spellcheck_and_search)
1081     AppendSpellcheckOptionsSubMenu();
1082   AppendSpeechInputOptionsSubMenu();
1083   AppendPlatformEditableItems();
1084
1085   menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1086   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SELECTALL,
1087                                   IDS_CONTENT_CONTEXT_SELECTALL);
1088 }
1089
1090 void RenderViewContextMenu::AppendSpellingSuggestionsSubMenu() {
1091   if (!spelling_menu_observer_.get())
1092     spelling_menu_observer_.reset(new SpellingMenuObserver(this));
1093   observers_.AddObserver(spelling_menu_observer_.get());
1094   spelling_menu_observer_->InitMenu(params_);
1095 }
1096
1097 void RenderViewContextMenu::AppendSpellcheckOptionsSubMenu() {
1098   if (!spellchecker_submenu_observer_.get()) {
1099     spellchecker_submenu_observer_.reset(new SpellCheckerSubMenuObserver(
1100         this, this, kSpellcheckRadioGroup));
1101   }
1102   spellchecker_submenu_observer_->InitMenu(params_);
1103   observers_.AddObserver(spellchecker_submenu_observer_.get());
1104 }
1105
1106 void RenderViewContextMenu::AppendSpeechInputOptionsSubMenu() {
1107   if (params_.speech_input_enabled) {
1108     speech_input_submenu_model_.AddCheckItem(
1109         IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES,
1110         l10n_util::GetStringUTF16(
1111             IDS_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES));
1112
1113     speech_input_submenu_model_.AddItemWithStringId(
1114         IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT,
1115         IDS_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT);
1116
1117     menu_model_.AddSubMenu(
1118         IDC_SPEECH_INPUT_MENU,
1119         l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPEECH_INPUT_MENU),
1120         &speech_input_submenu_model_);
1121   }
1122 }
1123
1124 void RenderViewContextMenu::AppendProtocolHandlerSubMenu() {
1125   const ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1126       GetHandlersForLinkUrl();
1127   if (handlers.empty())
1128     return;
1129   size_t max = IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST -
1130       IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
1131   for (size_t i = 0; i < handlers.size() && i <= max; i++) {
1132     protocol_handler_submenu_model_.AddItem(
1133         IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST + i,
1134         handlers[i].title());
1135   }
1136   protocol_handler_submenu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1137   protocol_handler_submenu_model_.AddItem(
1138       IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS,
1139       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH_CONFIGURE));
1140
1141   menu_model_.AddSubMenu(
1142       IDC_CONTENT_CONTEXT_OPENLINKWITH,
1143       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH),
1144       &protocol_handler_submenu_model_);
1145 }
1146
1147 void RenderViewContextMenu::AppendPlatformEditableItems() {
1148 }
1149
1150 // Menu delegate functions -----------------------------------------------------
1151
1152 bool RenderViewContextMenu::IsCommandIdEnabled(int id) const {
1153   // If this command is is added by one of our observers, we dispatch it to the
1154   // observer.
1155   ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
1156   RenderViewContextMenuObserver* observer;
1157   while ((observer = it.GetNext()) != NULL) {
1158     if (observer->IsCommandIdSupported(id))
1159       return observer->IsCommandIdEnabled(id);
1160   }
1161
1162   CoreTabHelper* core_tab_helper =
1163       CoreTabHelper::FromWebContents(source_web_contents_);
1164   int content_restrictions = 0;
1165   if (core_tab_helper)
1166     content_restrictions = core_tab_helper->content_restrictions();
1167   if (id == IDC_PRINT && (content_restrictions & CONTENT_RESTRICTION_PRINT))
1168     return false;
1169
1170   if (id == IDC_SAVE_PAGE &&
1171       (content_restrictions & CONTENT_RESTRICTION_SAVE)) {
1172     return false;
1173   }
1174
1175   // Allow Spell Check language items on sub menu for text area context menu.
1176   if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) &&
1177       (id < IDC_SPELLCHECK_LANGUAGES_LAST)) {
1178     return profile_->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck);
1179   }
1180
1181   // Custom items.
1182   if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1183       id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1184     return IsCustomItemEnabled(params_.custom_items, id);
1185   }
1186
1187   // Extension items.
1188   if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
1189       id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
1190     return extension_items_.IsCommandIdEnabled(id);
1191   }
1192
1193   if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
1194       id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
1195     return true;
1196   }
1197
1198   IncognitoModePrefs::Availability incognito_avail =
1199       IncognitoModePrefs::GetAvailability(profile_->GetPrefs());
1200   switch (id) {
1201     case IDC_BACK:
1202       return source_web_contents_->GetController().CanGoBack();
1203
1204     case IDC_FORWARD:
1205       return source_web_contents_->GetController().CanGoForward();
1206
1207     case IDC_RELOAD: {
1208       CoreTabHelper* core_tab_helper =
1209           CoreTabHelper::FromWebContents(source_web_contents_);
1210       if (!core_tab_helper)
1211         return false;
1212
1213       CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
1214       return !core_delegate ||
1215              core_delegate->CanReloadContents(source_web_contents_);
1216     }
1217
1218     case IDC_VIEW_SOURCE:
1219     case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
1220       return source_web_contents_->GetController().CanViewSource();
1221
1222     case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
1223     case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE:
1224     case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP:
1225     case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP:
1226       return IsDevCommandEnabled(id);
1227
1228     case IDC_CONTENT_CONTEXT_VIEWPAGEINFO:
1229       if (source_web_contents_->GetController().GetActiveEntry() == NULL)
1230         return false;
1231       // Disabled if no browser is associated (e.g. desktop notifications).
1232       if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL)
1233         return false;
1234       return true;
1235
1236     case IDC_CONTENT_CONTEXT_TRANSLATE: {
1237       TranslateTabHelper* translate_tab_helper =
1238           TranslateTabHelper::FromWebContents(source_web_contents_);
1239       if (!translate_tab_helper)
1240         return false;
1241       std::string original_lang =
1242           translate_tab_helper->language_state().original_language();
1243       std::string target_lang = g_browser_process->GetApplicationLocale();
1244       target_lang = TranslateManager::GetLanguageCode(target_lang);
1245       // Note that we intentionally enable the menu even if the original and
1246       // target languages are identical.  This is to give a way to user to
1247       // translate a page that might contains text fragments in a different
1248       // language.
1249       return ((params_.edit_flags & WebContextMenuData::CanTranslate) != 0) &&
1250              !original_lang.empty() &&  // Did we receive the page language yet?
1251              !translate_tab_helper->language_state().IsPageTranslated() &&
1252              !source_web_contents_->GetInterstitialPage() &&
1253              // There are some application locales which can't be used as a
1254              // target language for translation.
1255              TranslateManager::IsSupportedLanguage(target_lang) &&
1256              // Disable on the Instant Extended NTP.
1257              !chrome::IsInstantNTP(source_web_contents_);
1258     }
1259
1260     case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB:
1261     case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
1262       return params_.link_url.is_valid();
1263
1264     case IDC_CONTENT_CONTEXT_COPYLINKLOCATION:
1265       return params_.unfiltered_link_url.is_valid();
1266
1267     case IDC_CONTENT_CONTEXT_SAVELINKAS: {
1268       PrefService* local_state = g_browser_process->local_state();
1269       DCHECK(local_state);
1270       // Test if file-selection dialogs are forbidden by policy.
1271       if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1272         return false;
1273
1274       return params_.link_url.is_valid() &&
1275           ProfileIOData::IsHandledProtocol(params_.link_url.scheme());
1276     }
1277
1278     case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: {
1279       PrefService* local_state = g_browser_process->local_state();
1280       DCHECK(local_state);
1281       // Test if file-selection dialogs are forbidden by policy.
1282       if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1283         return false;
1284
1285       return params_.src_url.is_valid() &&
1286           ProfileIOData::IsHandledProtocol(params_.src_url.scheme());
1287     }
1288
1289     // The images shown in the most visited thumbnails can't be opened or
1290     // searched for conventionally.
1291     case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
1292     case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
1293       return params_.src_url.is_valid() &&
1294           (params_.src_url.scheme() != chrome::kChromeUIScheme);
1295
1296     case IDC_CONTENT_CONTEXT_COPYIMAGE:
1297       return params_.has_image_contents;
1298
1299     // Media control commands should all be disabled if the player is in an
1300     // error state.
1301     case IDC_CONTENT_CONTEXT_PLAYPAUSE:
1302     case IDC_CONTENT_CONTEXT_LOOP:
1303       return (params_.media_flags &
1304               WebContextMenuData::MediaInError) == 0;
1305
1306     // Mute and unmute should also be disabled if the player has no audio.
1307     case IDC_CONTENT_CONTEXT_MUTE:
1308       return (params_.media_flags &
1309               WebContextMenuData::MediaHasAudio) != 0 &&
1310              (params_.media_flags &
1311               WebContextMenuData::MediaInError) == 0;
1312
1313     // Media controls can be toggled only for video player. If we toggle
1314     // controls for audio then the player disappears, and there is no way to
1315     // return it back.
1316     case IDC_CONTENT_CONTEXT_CONTROLS:
1317       return (params_.media_flags &
1318               WebContextMenuData::MediaHasVideo) != 0;
1319
1320     case IDC_CONTENT_CONTEXT_ROTATECW:
1321     case IDC_CONTENT_CONTEXT_ROTATECCW:
1322       return
1323           (params_.media_flags & WebContextMenuData::MediaCanRotate) != 0;
1324
1325     case IDC_CONTENT_CONTEXT_COPYAVLOCATION:
1326     case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION:
1327       return params_.src_url.is_valid();
1328
1329     case IDC_CONTENT_CONTEXT_SAVEAVAS: {
1330       PrefService* local_state = g_browser_process->local_state();
1331       DCHECK(local_state);
1332       // Test if file-selection dialogs are forbidden by policy.
1333       if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1334         return false;
1335
1336       const GURL& url = params_.src_url;
1337       bool can_save =
1338           (params_.media_flags & WebContextMenuData::MediaCanSave) &&
1339           url.is_valid() && ProfileIOData::IsHandledProtocol(url.scheme());
1340 #if defined(ENABLE_FULL_PRINTING)
1341           // Do not save the preview PDF on the print preview page.
1342       can_save = can_save &&
1343           !(printing::PrintPreviewDialogController::IsPrintPreviewURL(url));
1344 #endif
1345       return can_save;
1346     }
1347
1348     case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
1349       return true;
1350
1351     case IDC_SAVE_PAGE: {
1352       CoreTabHelper* core_tab_helper =
1353           CoreTabHelper::FromWebContents(source_web_contents_);
1354       if (!core_tab_helper)
1355         return false;
1356
1357       CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
1358       if (core_delegate &&
1359           !core_delegate->CanSaveContents(source_web_contents_))
1360         return false;
1361
1362       PrefService* local_state = g_browser_process->local_state();
1363       DCHECK(local_state);
1364       // Test if file-selection dialogs are forbidden by policy.
1365       if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1366         return false;
1367
1368       // Instead of using GetURL here, we use url() (which is the "real" url of
1369       // the page) from the NavigationEntry because its reflects their origin
1370       // rather than the display one (returned by GetURL) which may be
1371       // different (like having "view-source:" on the front).
1372       // TODO(nasko): Audit all GetActiveEntry calls in this file.
1373       NavigationEntry* active_entry =
1374           source_web_contents_->GetController().GetActiveEntry();
1375       return content::IsSavableURL(
1376           (active_entry) ? active_entry->GetURL() : GURL());
1377     }
1378
1379     case IDC_CONTENT_CONTEXT_RELOADFRAME:
1380       return params_.frame_url.is_valid();
1381
1382     case IDC_CONTENT_CONTEXT_UNDO:
1383       return !!(params_.edit_flags & WebContextMenuData::CanUndo);
1384
1385     case IDC_CONTENT_CONTEXT_REDO:
1386       return !!(params_.edit_flags & WebContextMenuData::CanRedo);
1387
1388     case IDC_CONTENT_CONTEXT_CUT:
1389       return !!(params_.edit_flags & WebContextMenuData::CanCut);
1390
1391     case IDC_CONTENT_CONTEXT_COPY:
1392       return !!(params_.edit_flags & WebContextMenuData::CanCopy);
1393
1394     case IDC_CONTENT_CONTEXT_PASTE:
1395     case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE:
1396       return !!(params_.edit_flags & WebContextMenuData::CanPaste);
1397
1398     case IDC_CONTENT_CONTEXT_DELETE:
1399       return !!(params_.edit_flags & WebContextMenuData::CanDelete);
1400
1401     case IDC_CONTENT_CONTEXT_SELECTALL:
1402       return !!(params_.edit_flags & WebContextMenuData::CanSelectAll);
1403
1404     case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
1405       return !profile_->IsOffTheRecord() && params_.link_url.is_valid() &&
1406              incognito_avail != IncognitoModePrefs::DISABLED;
1407
1408     case IDC_PRINT:
1409       return profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) &&
1410           (params_.media_type == WebContextMenuData::MediaTypeNone ||
1411            params_.media_flags & WebContextMenuData::MediaCanPrint);
1412
1413     case IDC_CONTENT_CONTEXT_SEARCHWEBFOR:
1414     case IDC_CONTENT_CONTEXT_GOTOURL:
1415     case IDC_SPELLPANEL_TOGGLE:
1416     case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS:
1417       return true;
1418     case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO:
1419       // Disabled if no browser is associated (e.g. desktop notifications).
1420       if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL)
1421         return false;
1422       return true;
1423
1424     case IDC_CHECK_SPELLING_WHILE_TYPING:
1425       return profile_->GetPrefs()->GetBoolean(
1426           prefs::kEnableContinuousSpellcheck);
1427
1428 #if !defined(OS_MACOSX) && defined(OS_POSIX)
1429     // TODO(suzhe): this should not be enabled for password fields.
1430     case IDC_INPUT_METHODS_MENU:
1431       return true;
1432 #endif
1433
1434     case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE:
1435       return !params_.keyword_url.is_empty();
1436
1437     case IDC_SPELLCHECK_MENU:
1438       return true;
1439
1440     case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES:
1441     case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT:
1442     case IDC_SPEECH_INPUT_MENU:
1443       return true;
1444
1445     case IDC_CONTENT_CONTEXT_OPENLINKWITH:
1446       return true;
1447
1448     case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS:
1449       return true;
1450
1451     default:
1452       NOTREACHED();
1453       return false;
1454   }
1455 }
1456
1457 bool RenderViewContextMenu::IsCommandIdChecked(int id) const {
1458   // If this command is is added by one of our observers, we dispatch it to the
1459   // observer.
1460   ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
1461   RenderViewContextMenuObserver* observer;
1462   while ((observer = it.GetNext()) != NULL) {
1463     if (observer->IsCommandIdSupported(id))
1464       return observer->IsCommandIdChecked(id);
1465   }
1466
1467   // See if the video is set to looping.
1468   if (id == IDC_CONTENT_CONTEXT_LOOP) {
1469     return (params_.media_flags &
1470             WebContextMenuData::MediaLoop) != 0;
1471   }
1472
1473   if (id == IDC_CONTENT_CONTEXT_CONTROLS) {
1474     return (params_.media_flags &
1475             WebContextMenuData::MediaControls) != 0;
1476   }
1477
1478   // Custom items.
1479   if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1480       id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1481     return IsCustomItemChecked(params_.custom_items, id);
1482   }
1483
1484   // Extension items.
1485   if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
1486       id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
1487     return extension_items_.IsCommandIdChecked(id);
1488   }
1489
1490 #if defined(ENABLE_INPUT_SPEECH)
1491   // Check box for menu item 'Block offensive words'.
1492   if (id == IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES) {
1493     return profile_->GetPrefs()->GetBoolean(
1494         prefs::kSpeechRecognitionFilterProfanities);
1495   }
1496 #endif
1497
1498   return false;
1499 }
1500
1501 void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) {
1502   command_executed_ = true;
1503   // If this command is is added by one of our observers, we dispatch it to the
1504   // observer.
1505   ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
1506   RenderViewContextMenuObserver* observer;
1507   while ((observer = it.GetNext()) != NULL) {
1508     if (observer->IsCommandIdSupported(id))
1509       return observer->ExecuteCommand(id);
1510   }
1511
1512   RecordUsedItem(id);
1513
1514   RenderViewHost* rvh = source_web_contents_->GetRenderViewHost();
1515
1516   // Process custom actions range.
1517   if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1518       id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1519     unsigned action = id - IDC_CONTENT_CONTEXT_CUSTOM_FIRST;
1520     const content::CustomContextMenuContext& context = params_.custom_context;
1521 #if defined(ENABLE_PLUGINS)
1522     if (context.request_id && !context.is_pepper_menu) {
1523       ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins(
1524         rvh->GetProcess()->GetID());
1525     }
1526 #endif
1527     rvh->ExecuteCustomContextMenuCommand(action, context);
1528     return;
1529   }
1530
1531   // Process extension menu items.
1532   if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
1533       id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
1534     extension_items_.ExecuteCommand(id, source_web_contents_, params_);
1535     return;
1536   }
1537
1538   if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
1539       id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
1540     ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1541         GetHandlersForLinkUrl();
1542     if (handlers.empty()) {
1543       return;
1544     }
1545     content::RecordAction(
1546         UserMetricsAction("RegisterProtocolHandler.ContextMenu_Open"));
1547     int handlerIndex = id - IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
1548     WindowOpenDisposition disposition =
1549         ForceNewTabDispositionFromEventFlags(event_flags);
1550     OpenURL(
1551         handlers[handlerIndex].TranslateUrl(params_.link_url),
1552         params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1553         params_.frame_id,
1554         disposition,
1555         content::PAGE_TRANSITION_LINK);
1556     return;
1557   }
1558
1559   switch (id) {
1560     case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: {
1561       Browser* browser =
1562           chrome::FindBrowserWithWebContents(source_web_contents_);
1563       OpenURL(
1564           params_.link_url,
1565           params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1566           params_.frame_id,
1567           !browser || browser->is_app() ?
1568                   NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB,
1569           content::PAGE_TRANSITION_LINK);
1570       break;
1571     }
1572     case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
1573       OpenURL(
1574           params_.link_url,
1575           params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1576           params_.frame_id,
1577           NEW_WINDOW, content::PAGE_TRANSITION_LINK);
1578       break;
1579
1580     case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
1581       OpenURL(params_.link_url,
1582               GURL(),
1583               params_.frame_id,
1584               OFF_THE_RECORD,
1585               content::PAGE_TRANSITION_LINK);
1586       break;
1587
1588     case IDC_CONTENT_CONTEXT_SAVELINKAS: {
1589       RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
1590       const GURL& referrer =
1591           params_.frame_url.is_empty() ? params_.page_url : params_.frame_url;
1592       const GURL& url = params_.link_url;
1593       DownloadManager* dlm = BrowserContext::GetDownloadManager(profile_);
1594       scoped_ptr<DownloadUrlParameters> dl_params(
1595           DownloadUrlParameters::FromWebContents(source_web_contents_, url));
1596       dl_params->set_referrer(
1597           content::Referrer(referrer, params_.referrer_policy));
1598       dl_params->set_referrer_encoding(params_.frame_charset);
1599       dl_params->set_prompt(true);
1600       dlm->DownloadUrl(dl_params.Pass());
1601       break;
1602     }
1603
1604     case IDC_CONTENT_CONTEXT_SAVEAVAS:
1605     case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: {
1606       RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
1607       const GURL& referrer =
1608           params_.frame_url.is_empty() ? params_.page_url : params_.frame_url;
1609       const GURL& url = params_.src_url;
1610       int64 post_id = -1;
1611       if (url == source_web_contents_->GetURL()) {
1612         const NavigationEntry* entry =
1613             source_web_contents_->GetController().GetActiveEntry();
1614         if (entry)
1615           post_id = entry->GetPostID();
1616       }
1617       DownloadManager* dlm = BrowserContext::GetDownloadManager(profile_);
1618       scoped_ptr<DownloadUrlParameters> dl_params(
1619           DownloadUrlParameters::FromWebContents(source_web_contents_, url));
1620       dl_params->set_referrer(
1621           content::Referrer(referrer, params_.referrer_policy));
1622       dl_params->set_post_id(post_id);
1623       dl_params->set_prefer_cache(true);
1624       if (post_id >= 0)
1625         dl_params->set_method("POST");
1626       dl_params->set_prompt(true);
1627       dlm->DownloadUrl(dl_params.Pass());
1628       break;
1629     }
1630
1631     case IDC_CONTENT_CONTEXT_COPYLINKLOCATION:
1632       WriteURLToClipboard(params_.unfiltered_link_url);
1633       break;
1634
1635     case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION:
1636     case IDC_CONTENT_CONTEXT_COPYAVLOCATION:
1637       WriteURLToClipboard(params_.src_url);
1638       break;
1639
1640     case IDC_CONTENT_CONTEXT_COPYIMAGE:
1641       CopyImageAt(params_.x, params_.y);
1642       break;
1643
1644     case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
1645       GetImageThumbnailForSearch();
1646       break;
1647
1648     case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
1649     case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
1650       OpenURL(
1651           params_.src_url,
1652           params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1653           params_.frame_id,
1654           NEW_BACKGROUND_TAB, content::PAGE_TRANSITION_LINK);
1655       break;
1656
1657     case IDC_CONTENT_CONTEXT_PLAYPAUSE: {
1658       bool play = !!(params_.media_flags & WebContextMenuData::MediaPaused);
1659       if (play) {
1660         content::RecordAction(UserMetricsAction("MediaContextMenu_Play"));
1661       } else {
1662         content::RecordAction(UserMetricsAction("MediaContextMenu_Pause"));
1663       }
1664       MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1665                           WebMediaPlayerAction(
1666                               WebMediaPlayerAction::Play, play));
1667       break;
1668     }
1669
1670     case IDC_CONTENT_CONTEXT_MUTE: {
1671       bool mute = !(params_.media_flags & WebContextMenuData::MediaMuted);
1672       if (mute) {
1673         content::RecordAction(UserMetricsAction("MediaContextMenu_Mute"));
1674       } else {
1675         content::RecordAction(UserMetricsAction("MediaContextMenu_Unmute"));
1676       }
1677       MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1678                           WebMediaPlayerAction(
1679                               WebMediaPlayerAction::Mute, mute));
1680       break;
1681     }
1682
1683     case IDC_CONTENT_CONTEXT_LOOP:
1684       content::RecordAction(UserMetricsAction("MediaContextMenu_Loop"));
1685       MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1686                           WebMediaPlayerAction(
1687                               WebMediaPlayerAction::Loop,
1688                               !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP)));
1689       break;
1690
1691     case IDC_CONTENT_CONTEXT_CONTROLS:
1692       content::RecordAction(UserMetricsAction("MediaContextMenu_Controls"));
1693       MediaPlayerActionAt(
1694           gfx::Point(params_.x, params_.y),
1695           WebMediaPlayerAction(
1696               WebMediaPlayerAction::Controls,
1697               !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS)));
1698       break;
1699
1700     case IDC_CONTENT_CONTEXT_ROTATECW:
1701       content::RecordAction(
1702       UserMetricsAction("PluginContextMenu_RotateClockwise"));
1703       PluginActionAt(
1704           gfx::Point(params_.x, params_.y),
1705           WebPluginAction(
1706               WebPluginAction::Rotate90Clockwise,
1707               true));
1708       break;
1709
1710     case IDC_CONTENT_CONTEXT_ROTATECCW:
1711       content::RecordAction(
1712       UserMetricsAction("PluginContextMenu_RotateCounterclockwise"));
1713       PluginActionAt(
1714           gfx::Point(params_.x, params_.y),
1715           WebPluginAction(
1716               WebPluginAction::Rotate90Counterclockwise,
1717               true));
1718       break;
1719
1720     case IDC_BACK:
1721       source_web_contents_->GetController().GoBack();
1722       break;
1723
1724     case IDC_FORWARD:
1725       source_web_contents_->GetController().GoForward();
1726       break;
1727
1728     case IDC_SAVE_PAGE:
1729       source_web_contents_->OnSavePage();
1730       break;
1731
1732     case IDC_RELOAD:
1733       // Prevent the modal "Resubmit form post" dialog from appearing in the
1734       // context of an external context menu.
1735       source_web_contents_->GetController().Reload(!external_);
1736       break;
1737
1738     case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP: {
1739       const Extension* platform_app = GetExtension();
1740       DCHECK(platform_app);
1741       DCHECK(platform_app->is_platform_app());
1742
1743       extensions::ExtensionSystem::Get(profile_)->extension_service()->
1744           ReloadExtension(platform_app->id());
1745       break;
1746     }
1747
1748     case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP: {
1749       const Extension* platform_app = GetExtension();
1750       DCHECK(platform_app);
1751       DCHECK(platform_app->is_platform_app());
1752
1753       apps::AppLoadService::Get(profile_)->RestartApplication(
1754           platform_app->id());
1755       break;
1756     }
1757
1758     case IDC_PRINT:
1759 #if defined(ENABLE_PRINTING)
1760       if (params_.media_type == WebContextMenuData::MediaTypeNone) {
1761 #if defined(ENABLE_FULL_PRINTING)
1762         printing::PrintViewManager* print_view_manager =
1763             printing::PrintViewManager::FromWebContents(source_web_contents_);
1764
1765         if (!print_view_manager)
1766           break;
1767         if (profile_->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled)) {
1768           print_view_manager->PrintNow();
1769         } else {
1770           print_view_manager->PrintPreviewNow(!params_.selection_text.empty());
1771         }
1772 #else
1773         printing::PrintViewManagerBasic* print_view_manager =
1774             printing::PrintViewManagerBasic::FromWebContents(
1775                 source_web_contents_);
1776         if (!print_view_manager)
1777           break;
1778         print_view_manager->PrintNow();
1779 #endif  // defined(ENABLE_FULL_PRINTING)
1780       } else {
1781         rvh->Send(new PrintMsg_PrintNodeUnderContextMenu(rvh->GetRoutingID()));
1782       }
1783 #endif  // defined(ENABLE_PRINTING)
1784       break;
1785
1786     case IDC_VIEW_SOURCE:
1787       source_web_contents_->ViewSource();
1788       break;
1789
1790     case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
1791       Inspect(params_.x, params_.y);
1792       break;
1793
1794     case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE: {
1795       const Extension* platform_app = GetExtension();
1796       DCHECK(platform_app);
1797       DCHECK(platform_app->is_platform_app());
1798
1799       extensions::devtools_util::InspectBackgroundPage(platform_app, profile_);
1800       break;
1801     }
1802
1803     case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: {
1804       NavigationController* controller = &source_web_contents_->GetController();
1805       // Important to use GetVisibleEntry to match what's showing in the
1806       // omnibox.
1807       NavigationEntry* nav_entry = controller->GetVisibleEntry();
1808       Browser* browser =
1809           chrome::FindBrowserWithWebContents(source_web_contents_);
1810       chrome::ShowWebsiteSettings(browser, source_web_contents_,
1811                                   nav_entry->GetURL(), nav_entry->GetSSL());
1812       break;
1813     }
1814
1815     case IDC_CONTENT_CONTEXT_TRANSLATE: {
1816       // A translation might have been triggered by the time the menu got
1817       // selected, do nothing in that case.
1818       TranslateTabHelper* translate_tab_helper =
1819           TranslateTabHelper::FromWebContents(source_web_contents_);
1820       if (!translate_tab_helper ||
1821           translate_tab_helper->language_state().IsPageTranslated() ||
1822           translate_tab_helper->language_state().translation_pending()) {
1823         return;
1824       }
1825       std::string original_lang =
1826           translate_tab_helper->language_state().original_language();
1827       std::string target_lang = g_browser_process->GetApplicationLocale();
1828       target_lang = TranslateManager::GetLanguageCode(target_lang);
1829       // Since the user decided to translate for that language and site, clears
1830       // any preferences for not translating them.
1831       TranslatePrefs prefs(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;
1837     }
1838
1839     case IDC_CONTENT_CONTEXT_RELOADFRAME:
1840       rvh->ReloadFrame();
1841       break;
1842
1843     case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
1844       source_web_contents_->ViewFrameSource(params_.frame_url,
1845                                             params_.frame_page_state);
1846       break;
1847
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;
1854     }
1855
1856     case IDC_CONTENT_CONTEXT_UNDO:
1857       rvh->Undo();
1858       break;
1859
1860     case IDC_CONTENT_CONTEXT_REDO:
1861       rvh->Redo();
1862       break;
1863
1864     case IDC_CONTENT_CONTEXT_CUT:
1865       rvh->Cut();
1866       break;
1867
1868     case IDC_CONTENT_CONTEXT_COPY:
1869       rvh->Copy();
1870       break;
1871
1872     case IDC_CONTENT_CONTEXT_PASTE:
1873       rvh->Paste();
1874       break;
1875
1876     case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE:
1877       rvh->PasteAndMatchStyle();
1878       break;
1879
1880     case IDC_CONTENT_CONTEXT_DELETE:
1881       rvh->Delete();
1882       break;
1883
1884     case IDC_CONTENT_CONTEXT_SELECTALL:
1885       rvh->SelectAll();
1886       break;
1887
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;
1898     }
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;
1906     }
1907
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;
1917     }
1918
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();
1926
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         string16 keyword(TemplateURLService::GenerateKeyword(params_.page_url));
1932         TemplateURLData data;
1933         data.short_name = keyword;
1934         data.SetKeyword(keyword);
1935         data.SetURL(params_.keyword_url.spec());
1936         data.favicon_url =
1937             TemplateURL::GenerateFaviconURL(params_.page_url.GetOrigin());
1938         // Takes ownership of the TemplateURL.
1939         search_engine_tab_helper->delegate()->
1940             ConfirmAddSearchProvider(new TemplateURL(profile_, data), profile_);
1941       }
1942       break;
1943     }
1944
1945 #if defined(ENABLE_INPUT_SPEECH)
1946     case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES: {
1947       profile_->GetPrefs()->SetBoolean(
1948           prefs::kSpeechRecognitionFilterProfanities,
1949           !profile_->GetPrefs()->GetBoolean(
1950               prefs::kSpeechRecognitionFilterProfanities));
1951       break;
1952     }
1953 #endif
1954     case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT: {
1955       GURL url(chrome::kSpeechInputAboutURL);
1956       GURL localized_url = google_util::AppendGoogleLocaleParam(url);
1957       // Open URL with no referrer field (because user clicked on menu item).
1958       OpenURL(localized_url, GURL(), 0, NEW_FOREGROUND_TAB,
1959           content::PAGE_TRANSITION_LINK);
1960       break;
1961     }
1962
1963     default:
1964       NOTREACHED();
1965       break;
1966   }
1967 }
1968
1969 ProtocolHandlerRegistry::ProtocolHandlerList
1970     RenderViewContextMenu::GetHandlersForLinkUrl() {
1971   ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1972       protocol_handler_registry_->GetHandlersFor(params_.link_url.scheme());
1973   std::sort(handlers.begin(), handlers.end());
1974   return handlers;
1975 }
1976
1977 void RenderViewContextMenu::MenuWillShow(ui::SimpleMenuModel* source) {
1978   for (int i = 0; i < source->GetItemCount(); ++i) {
1979     if (source->IsVisibleAt(i) &&
1980         source->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR) {
1981       RecordShownItem(source->GetCommandIdAt(i));
1982     }
1983   }
1984
1985   // Ignore notifications from submenus.
1986   if (source != &menu_model_)
1987     return;
1988
1989   content::RenderWidgetHostView* view =
1990       source_web_contents_->GetRenderWidgetHostView();
1991   if (view)
1992     view->SetShowingContextMenu(true);
1993
1994   content::NotificationService::current()->Notify(
1995       chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN,
1996       content::Source<RenderViewContextMenu>(this),
1997       content::NotificationService::NoDetails());
1998 }
1999
2000 void RenderViewContextMenu::MenuClosed(ui::SimpleMenuModel* source) {
2001   // Ignore notifications from submenus.
2002   if (source != &menu_model_)
2003     return;
2004
2005   content::RenderWidgetHostView* view =
2006       source_web_contents_->GetRenderWidgetHostView();
2007   if (view)
2008     view->SetShowingContextMenu(false);
2009   RenderViewHost* rvh = source_web_contents_->GetRenderViewHost();
2010   if (rvh) {
2011     rvh->NotifyContextMenuClosed(params_.custom_context);
2012   }
2013
2014   if (!command_executed_) {
2015     FOR_EACH_OBSERVER(RenderViewContextMenuObserver,
2016                       observers_,
2017                       OnMenuCancel());
2018   }
2019 }
2020
2021 bool RenderViewContextMenu::IsDevCommandEnabled(int id) const {
2022   if (id == IDC_CONTENT_CONTEXT_INSPECTELEMENT ||
2023       id == IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE) {
2024     const CommandLine* command_line = CommandLine::ForCurrentProcess();
2025     if (!profile_->GetPrefs()->GetBoolean(prefs::kWebKitJavascriptEnabled) ||
2026         command_line->HasSwitch(switches::kDisableJavaScript))
2027       return false;
2028
2029     // Don't enable the web inspector if the developer tools are disabled via
2030     // the preference dev-tools-disabled.
2031     if (profile_->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled))
2032       return false;
2033   }
2034
2035   return true;
2036 }
2037
2038 string16 RenderViewContextMenu::PrintableSelectionText() {
2039   return gfx::TruncateString(params_.selection_text,
2040                             kMaxSelectionTextLength);
2041 }
2042
2043 // Controller functions --------------------------------------------------------
2044
2045 void RenderViewContextMenu::OpenURL(
2046     const GURL& url, const GURL& referrer, int64 frame_id,
2047     WindowOpenDisposition disposition,
2048     content::PageTransition transition) {
2049   // Ensure that URL fragment, username and password fields are not sent
2050   // in the referrer.
2051   GURL sanitized_referrer(referrer);
2052   if (sanitized_referrer.is_valid() && (sanitized_referrer.has_ref() ||
2053       sanitized_referrer.has_username() || sanitized_referrer.has_password())) {
2054     GURL::Replacements referrer_mods;
2055     referrer_mods.ClearRef();
2056     referrer_mods.ClearUsername();
2057     referrer_mods.ClearPassword();
2058     sanitized_referrer = sanitized_referrer.ReplaceComponents(referrer_mods);
2059   }
2060
2061   WebContents* new_contents = source_web_contents_->OpenURL(OpenURLParams(
2062       url, content::Referrer(sanitized_referrer, params_.referrer_policy),
2063       disposition, transition, false));
2064   if (!new_contents)
2065     return;
2066
2067   RetargetingDetails details;
2068   details.source_web_contents = source_web_contents_;
2069   details.source_frame_id = frame_id;
2070   details.target_url = url;
2071   details.target_web_contents = new_contents;
2072   details.not_yet_in_tabstrip = false;
2073   content::NotificationService::current()->Notify(
2074       chrome::NOTIFICATION_RETARGETING,
2075       content::Source<Profile>(Profile::FromBrowserContext(
2076           source_web_contents_->GetBrowserContext())),
2077       content::Details<RetargetingDetails>(&details));
2078 }
2079
2080 void RenderViewContextMenu::CopyImageAt(int x, int y) {
2081   source_web_contents_->GetRenderViewHost()->CopyImageAt(x, y);
2082 }
2083
2084 void RenderViewContextMenu::GetImageThumbnailForSearch() {
2085   source_web_contents_->GetRenderViewHost()->Send(
2086        new ChromeViewMsg_RequestThumbnailForContextNode(
2087            source_web_contents_->GetRenderViewHost()->GetRoutingID(),
2088            kImageSearchThumbnailMinSize,
2089            gfx::Size(kImageSearchThumbnailMaxWidth,
2090                      kImageSearchThumbnailMaxHeight)));
2091 }
2092
2093 void RenderViewContextMenu::Inspect(int x, int y) {
2094   content::RecordAction(UserMetricsAction("DevTools_InspectElement"));
2095   source_web_contents_->GetRenderViewHostAtPosition(
2096       x, y, base::Bind(&DevToolsInspectElementAt));
2097 }
2098
2099 void RenderViewContextMenu::WriteURLToClipboard(const GURL& url) {
2100   chrome_common_net::WriteURLToClipboard(
2101       url,
2102       profile_->GetPrefs()->GetString(prefs::kAcceptLanguages),
2103       ui::Clipboard::GetForCurrentThread());
2104 }
2105
2106 void RenderViewContextMenu::MediaPlayerActionAt(
2107     const gfx::Point& location,
2108     const WebMediaPlayerAction& action) {
2109   source_web_contents_->GetRenderViewHost()->
2110       ExecuteMediaPlayerActionAtLocation(location, action);
2111 }
2112
2113 void RenderViewContextMenu::PluginActionAt(
2114     const gfx::Point& location,
2115     const WebPluginAction& action) {
2116   source_web_contents_->GetRenderViewHost()->
2117       ExecutePluginActionAtLocation(location, action);
2118 }