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