| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/tab_contents/render_view_context_menu.h" |
| |
| #include <algorithm> |
| #include <set> |
| #include <utility> |
| |
| #include "apps/app_load_service.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram.h" |
| #include "base/prefs/pref_member.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/app_mode/app_mode_utils.h" |
| #include "chrome/browser/autocomplete/autocomplete_classifier.h" |
| #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" |
| #include "chrome/browser/autocomplete/autocomplete_match.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" |
| #include "chrome/browser/devtools/devtools_window.h" |
| #include "chrome/browser/download/download_service.h" |
| #include "chrome/browser/download/download_service_factory.h" |
| #include "chrome/browser/download/download_util.h" |
| #include "chrome/browser/extensions/extension_host.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_system.h" |
| #include "chrome/browser/google/google_util.h" |
| #include "chrome/browser/plugins/chrome_plugin_service_filter.h" |
| #include "chrome/browser/prefs/incognito_mode_prefs.h" |
| #include "chrome/browser/printing/print_preview_context_menu_observer.h" |
| #include "chrome/browser/printing/print_preview_dialog_controller.h" |
| #include "chrome/browser/printing/print_view_manager.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_io_data.h" |
| #include "chrome/browser/search/search.h" |
| #include "chrome/browser/search_engines/template_url.h" |
| #include "chrome/browser/search_engines/template_url_service.h" |
| #include "chrome/browser/search_engines/template_url_service_factory.h" |
| #include "chrome/browser/spellchecker/spellcheck_host_metrics.h" |
| #include "chrome/browser/spellchecker/spellcheck_service.h" |
| #include "chrome/browser/tab_contents/retargeting_details.h" |
| #include "chrome/browser/tab_contents/spellchecker_submenu_observer.h" |
| #include "chrome/browser/tab_contents/spelling_menu_observer.h" |
| #include "chrome/browser/translate/translate_manager.h" |
| #include "chrome/browser/translate/translate_prefs.h" |
| #include "chrome/browser/translate/translate_tab_helper.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h" |
| #include "chrome/browser/ui/tab_contents/core_tab_helper.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/content_restriction.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "chrome/common/net/url_util.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/print_messages.h" |
| #include "chrome/common/spellcheck_messages.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/public/browser/child_process_security_policy.h" |
| #include "content/public/browser/download_manager.h" |
| #include "content/public/browser/download_save_info.h" |
| #include "content/public/browser/download_url_parameters.h" |
| #include "content/public/browser/navigation_details.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host_view.h" |
| #include "content/public/browser/user_metrics.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/menu_item.h" |
| #include "content/public/common/ssl_status.h" |
| #include "content/public/common/url_utils.h" |
| #include "extensions/browser/view_type_utils.h" |
| #include "grit/generated_resources.h" |
| #include "net/base/escape.h" |
| #include "third_party/WebKit/public/web/WebContextMenuData.h" |
| #include "third_party/WebKit/public/web/WebMediaPlayerAction.h" |
| #include "third_party/WebKit/public/web/WebPluginAction.h" |
| #include "ui/base/clipboard/clipboard.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/text/text_elider.h" |
| #include "ui/gfx/favicon_size.h" |
| |
| using WebKit::WebContextMenuData; |
| using WebKit::WebMediaPlayerAction; |
| using WebKit::WebPluginAction; |
| using WebKit::WebString; |
| using WebKit::WebURL; |
| using content::BrowserContext; |
| using content::ChildProcessSecurityPolicy; |
| using content::DownloadManager; |
| using content::DownloadUrlParameters; |
| using content::NavigationController; |
| using content::NavigationEntry; |
| using content::OpenURLParams; |
| using content::RenderViewHost; |
| using content::SSLStatus; |
| using content::UserMetricsAction; |
| using content::WebContents; |
| using extensions::Extension; |
| using extensions::MenuItem; |
| using extensions::MenuManager; |
| |
| namespace { |
| |
| // Maps UMA enumeration to IDC. IDC could be changed so we can't use |
| // just them and |UMA_HISTOGRAM_CUSTOM_ENUMERATION|. |
| // Never change mapping or reuse |enum_id|. Always push back new items. |
| // Items that is not used any more by |RenderViewContextMenu.ExecuteCommand| |
| // could be deleted, but don't change the rest of |kUmaEnumToControlId|. |
| const struct UmaEnumCommandIdPair { |
| int enum_id; |
| int control_id; |
| } kUmaEnumToControlId[] = { |
| { 0, IDC_CONTENT_CONTEXT_CUSTOM_FIRST }, |
| { 1, IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST }, |
| { 2, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST }, |
| { 3, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB }, |
| { 4, IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW }, |
| { 5, IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD }, |
| { 6, IDC_CONTENT_CONTEXT_SAVELINKAS }, |
| { 7, IDC_CONTENT_CONTEXT_SAVEAVAS }, |
| { 8, IDC_CONTENT_CONTEXT_SAVEIMAGEAS }, |
| { 9, IDC_CONTENT_CONTEXT_COPYLINKLOCATION }, |
| { 10, IDC_CONTENT_CONTEXT_COPYIMAGELOCATION }, |
| { 11, IDC_CONTENT_CONTEXT_COPYAVLOCATION }, |
| { 12, IDC_CONTENT_CONTEXT_COPYIMAGE }, |
| { 13, IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB }, |
| { 14, IDC_CONTENT_CONTEXT_OPENAVNEWTAB }, |
| { 15, IDC_CONTENT_CONTEXT_PLAYPAUSE }, |
| { 16, IDC_CONTENT_CONTEXT_MUTE }, |
| { 17, IDC_CONTENT_CONTEXT_LOOP }, |
| { 18, IDC_CONTENT_CONTEXT_CONTROLS }, |
| { 19, IDC_CONTENT_CONTEXT_ROTATECW }, |
| { 20, IDC_CONTENT_CONTEXT_ROTATECCW }, |
| { 21, IDC_BACK }, |
| { 22, IDC_FORWARD }, |
| { 23, IDC_SAVE_PAGE }, |
| { 24, IDC_RELOAD }, |
| { 25, IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP }, |
| { 26, IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP }, |
| { 27, IDC_PRINT }, |
| { 28, IDC_VIEW_SOURCE }, |
| { 29, IDC_CONTENT_CONTEXT_INSPECTELEMENT }, |
| { 30, IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE }, |
| { 31, IDC_CONTENT_CONTEXT_VIEWPAGEINFO }, |
| { 32, IDC_CONTENT_CONTEXT_TRANSLATE }, |
| { 33, IDC_CONTENT_CONTEXT_RELOADFRAME }, |
| { 34, IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE }, |
| { 35, IDC_CONTENT_CONTEXT_VIEWFRAMEINFO }, |
| { 36, IDC_CONTENT_CONTEXT_UNDO }, |
| { 37, IDC_CONTENT_CONTEXT_REDO }, |
| { 38, IDC_CONTENT_CONTEXT_CUT }, |
| { 39, IDC_CONTENT_CONTEXT_COPY }, |
| { 40, IDC_CONTENT_CONTEXT_PASTE }, |
| { 41, IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE }, |
| { 42, IDC_CONTENT_CONTEXT_DELETE }, |
| { 43, IDC_CONTENT_CONTEXT_SELECTALL }, |
| { 44, IDC_CONTENT_CONTEXT_SEARCHWEBFOR }, |
| { 45, IDC_CONTENT_CONTEXT_GOTOURL }, |
| { 46, IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS }, |
| { 47, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS }, |
| { 48, IDC_CONTENT_CONTEXT_ADDSEARCHENGINE }, |
| { 49, IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES }, |
| { 50, IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT }, |
| { 51, IDC_SPEECH_INPUT_MENU }, |
| { 52, IDC_CONTENT_CONTEXT_OPENLINKWITH }, |
| { 53, IDC_CHECK_SPELLING_WHILE_TYPING }, |
| { 54, IDC_SPELLCHECK_MENU }, |
| { 55, IDC_CONTENT_CONTEXT_SPELLING_TOGGLE }, |
| { 56, IDC_SPELLCHECK_LANGUAGES_FIRST }, |
| // Add new items here and use |enum_id| from the next line. |
| { 57, 0 }, // Must be the last. Increment |enum_id| when new IDC was added. |
| }; |
| |
| // Collapses large ranges of ids before looking for UMA enum. |
| int CollapleCommandsForUMA(int id) { |
| if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { |
| return IDC_CONTENT_CONTEXT_CUSTOM_FIRST; |
| } |
| |
| if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { |
| return IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; |
| } |
| |
| if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST && |
| id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) { |
| return IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST; |
| } |
| |
| if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST && |
| id <= IDC_SPELLCHECK_LANGUAGES_LAST) { |
| return IDC_SPELLCHECK_LANGUAGES_FIRST; |
| } |
| |
| return id; |
| } |
| |
| // Returns UMA enum value for command specified by |id| or -1 if not found. |
| int FindUMAEnumValueForCommand(int id) { |
| id = CollapleCommandsForUMA(id); |
| const size_t kMappingSize = arraysize(kUmaEnumToControlId); |
| for (size_t i = 0; i < kMappingSize; ++i) { |
| if (kUmaEnumToControlId[i].control_id == id) { |
| return kUmaEnumToControlId[i].enum_id; |
| } |
| } |
| return -1; |
| } |
| |
| // Increments histogram value for used items specified by |id|. |
| void RecordUsedItem(int id) { |
| int enum_id = FindUMAEnumValueForCommand(id); |
| if (enum_id != -1) { |
| const size_t kMappingSize = arraysize(kUmaEnumToControlId); |
| UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Used", enum_id, |
| kUmaEnumToControlId[kMappingSize - 1].enum_id); |
| } else { |
| NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id; |
| } |
| } |
| |
| // Increments histogram value for visible context menu item specified by |id|. |
| void RecordShownItem(int id) { |
| int enum_id = FindUMAEnumValueForCommand(id); |
| if (enum_id != -1) { |
| const size_t kMappingSize = arraysize(kUmaEnumToControlId); |
| UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Shown", enum_id, |
| kUmaEnumToControlId[kMappingSize - 1].enum_id); |
| } else { |
| // Just warning here. It's harder to maintain list of all possibly |
| // visible items than executable items. |
| DLOG(ERROR) << "Update kUmaEnumToControlId. Unhanded IDC: " << id; |
| } |
| } |
| |
| // Usually a new tab is expected where this function is used, |
| // however users should be able to open a tab in background |
| // or in a new window. |
| WindowOpenDisposition ForceNewTabDispositionFromEventFlags( |
| int event_flags) { |
| WindowOpenDisposition disposition = |
| ui::DispositionFromEventFlags(event_flags); |
| return disposition == CURRENT_TAB ? NEW_FOREGROUND_TAB : disposition; |
| } |
| |
| bool IsCustomItemEnabled(const std::vector<content::MenuItem>& items, int id) { |
| DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST); |
| for (size_t i = 0; i < items.size(); ++i) { |
| int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action; |
| if (action_id == id) |
| return items[i].enabled; |
| if (items[i].type == content::MenuItem::SUBMENU) { |
| if (IsCustomItemEnabled(items[i].submenu, id)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool IsCustomItemChecked(const std::vector<content::MenuItem>& items, int id) { |
| DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST); |
| for (size_t i = 0; i < items.size(); ++i) { |
| int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action; |
| if (action_id == id) |
| return items[i].checked; |
| if (items[i].type == content::MenuItem::SUBMENU) { |
| if (IsCustomItemChecked(items[i].submenu, id)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| const size_t kMaxCustomMenuDepth = 5; |
| const size_t kMaxCustomMenuTotalItems = 1000; |
| |
| void AddCustomItemsToMenu(const std::vector<content::MenuItem>& items, |
| size_t depth, |
| size_t* total_items, |
| ui::SimpleMenuModel::Delegate* delegate, |
| ui::SimpleMenuModel* menu_model) { |
| if (depth > kMaxCustomMenuDepth) { |
| LOG(ERROR) << "Custom menu too deeply nested."; |
| return; |
| } |
| for (size_t i = 0; i < items.size(); ++i) { |
| if (IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action >= |
| IDC_CONTENT_CONTEXT_CUSTOM_LAST) { |
| LOG(ERROR) << "Custom menu action value too big."; |
| return; |
| } |
| if (*total_items >= kMaxCustomMenuTotalItems) { |
| LOG(ERROR) << "Custom menu too large (too many items)."; |
| return; |
| } |
| (*total_items)++; |
| switch (items[i].type) { |
| case content::MenuItem::OPTION: |
| menu_model->AddItem( |
| items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST, |
| items[i].label); |
| break; |
| case content::MenuItem::CHECKABLE_OPTION: |
| menu_model->AddCheckItem( |
| items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST, |
| items[i].label); |
| break; |
| case content::MenuItem::GROUP: |
| // TODO(viettrungluu): I don't know what this is supposed to do. |
| NOTREACHED(); |
| break; |
| case content::MenuItem::SEPARATOR: |
| menu_model->AddSeparator(ui::NORMAL_SEPARATOR); |
| break; |
| case content::MenuItem::SUBMENU: { |
| ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate); |
| AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate, |
| submenu); |
| menu_model->AddSubMenu( |
| items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST, |
| items[i].label, |
| submenu); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| } |
| |
| void DevToolsInspectElementAt(RenderViewHost* rvh, int x, int y) { |
| DevToolsWindow::InspectElement(rvh, x, y); |
| } |
| |
| // Helper function to escape "&" as "&&". |
| void EscapeAmpersands(string16* text) { |
| const char16 ampersand[] = {'&', 0}; |
| ReplaceChars(*text, ampersand, ASCIIToUTF16("&&"), text); |
| } |
| |
| } // namespace |
| |
| // static |
| const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50; |
| |
| // static |
| bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) { |
| return url.SchemeIs(chrome::kChromeDevToolsScheme); |
| } |
| |
| // static |
| bool RenderViewContextMenu::IsInternalResourcesURL(const GURL& url) { |
| if (!url.SchemeIs(chrome::kChromeUIScheme)) |
| return false; |
| return url.host() == chrome::kChromeUISyncResourcesHost; |
| } |
| |
| static const int kSpellcheckRadioGroup = 1; |
| |
| RenderViewContextMenu::RenderViewContextMenu( |
| WebContents* web_contents, |
| const content::ContextMenuParams& params) |
| : params_(params), |
| source_web_contents_(web_contents), |
| profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())), |
| menu_model_(this), |
| extension_items_(profile_, this, &menu_model_, |
| base::Bind(MenuItemMatchesParams, params_)), |
| external_(false), |
| speech_input_submenu_model_(this), |
| protocol_handler_submenu_model_(this), |
| protocol_handler_registry_( |
| ProtocolHandlerRegistryFactory::GetForProfile(profile_)), |
| command_executed_(false), |
| is_guest_(false) { |
| RenderViewHost* rvh = source_web_contents_->GetRenderViewHost(); |
| if (rvh && rvh->GetProcess()->IsGuest()) |
| is_guest_ = true; |
| } |
| |
| RenderViewContextMenu::~RenderViewContextMenu() { |
| } |
| |
| // Menu construction functions ------------------------------------------------- |
| |
| void RenderViewContextMenu::Init() { |
| InitMenu(); |
| PlatformInit(); |
| } |
| |
| void RenderViewContextMenu::Cancel() { |
| PlatformCancel(); |
| } |
| |
| static bool ExtensionPatternMatch(const extensions::URLPatternSet& patterns, |
| const GURL& url) { |
| // No patterns means no restriction, so that implicitly matches. |
| if (patterns.is_empty()) |
| return true; |
| return patterns.MatchesURL(url); |
| } |
| |
| // static |
| bool RenderViewContextMenu::ExtensionContextAndPatternMatch( |
| const content::ContextMenuParams& params, |
| MenuItem::ContextList contexts, |
| const extensions::URLPatternSet& target_url_patterns) { |
| const bool has_link = !params.link_url.is_empty(); |
| const bool has_selection = !params.selection_text.empty(); |
| const bool in_frame = !params.frame_url.is_empty(); |
| |
| if (contexts.Contains(MenuItem::ALL) || |
| (has_selection && contexts.Contains(MenuItem::SELECTION)) || |
| (params.is_editable && contexts.Contains(MenuItem::EDITABLE)) || |
| (in_frame && contexts.Contains(MenuItem::FRAME))) |
| return true; |
| |
| if (has_link && contexts.Contains(MenuItem::LINK) && |
| ExtensionPatternMatch(target_url_patterns, params.link_url)) |
| return true; |
| |
| switch (params.media_type) { |
| case WebContextMenuData::MediaTypeImage: |
| if (contexts.Contains(MenuItem::IMAGE) && |
| ExtensionPatternMatch(target_url_patterns, params.src_url)) |
| return true; |
| break; |
| |
| case WebContextMenuData::MediaTypeVideo: |
| if (contexts.Contains(MenuItem::VIDEO) && |
| ExtensionPatternMatch(target_url_patterns, params.src_url)) |
| return true; |
| break; |
| |
| case WebContextMenuData::MediaTypeAudio: |
| if (contexts.Contains(MenuItem::AUDIO) && |
| ExtensionPatternMatch(target_url_patterns, params.src_url)) |
| return true; |
| break; |
| |
| default: |
| break; |
| } |
| |
| // PAGE is the least specific context, so we only examine that if none of the |
| // other contexts apply (except for FRAME, which is included in PAGE for |
| // backwards compatibility). |
| if (!has_link && !has_selection && !params.is_editable && |
| params.media_type == WebContextMenuData::MediaTypeNone && |
| contexts.Contains(MenuItem::PAGE)) |
| return true; |
| |
| return false; |
| } |
| |
| static const GURL& GetDocumentURL(const content::ContextMenuParams& params) { |
| return params.frame_url.is_empty() ? params.page_url : params.frame_url; |
| } |
| |
| // static |
| bool RenderViewContextMenu::MenuItemMatchesParams( |
| const content::ContextMenuParams& params, |
| const extensions::MenuItem* item) { |
| bool match = ExtensionContextAndPatternMatch(params, item->contexts(), |
| item->target_url_patterns()); |
| if (!match) |
| return false; |
| |
| const GURL& document_url = GetDocumentURL(params); |
| return ExtensionPatternMatch(item->document_url_patterns(), document_url); |
| } |
| |
| void RenderViewContextMenu::AppendAllExtensionItems() { |
| extension_items_.Clear(); |
| ExtensionService* service = |
| extensions::ExtensionSystem::Get(profile_)->extension_service(); |
| if (!service) |
| return; // In unit-tests, we may not have an ExtensionService. |
| MenuManager* menu_manager = service->menu_manager(); |
| |
| string16 printable_selection_text = PrintableSelectionText(); |
| EscapeAmpersands(&printable_selection_text); |
| |
| // Get a list of extension id's that have context menu items, and sort by the |
| // top level context menu title of the extension. |
| std::set<std::string> ids = menu_manager->ExtensionIds(); |
| std::vector<base::string16> sorted_menu_titles; |
| std::map<base::string16, std::string> map_ids; |
| for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) { |
| const Extension* extension = service->GetExtensionById(*i, false); |
| // Platform apps have their context menus created directly in |
| // AppendPlatformAppItems. |
| if (extension && !extension->is_platform_app()) { |
| base::string16 menu_title = extension_items_.GetTopLevelContextMenuTitle( |
| *i, printable_selection_text); |
| map_ids[menu_title] = *i; |
| sorted_menu_titles.push_back(menu_title); |
| } |
| } |
| if (sorted_menu_titles.empty()) |
| return; |
| |
| const std::string app_locale = g_browser_process->GetApplicationLocale(); |
| l10n_util::SortStrings16(app_locale, &sorted_menu_titles); |
| |
| int index = 0; |
| base::TimeTicks begin = base::TimeTicks::Now(); |
| for (size_t i = 0; i < sorted_menu_titles.size(); ++i) { |
| const std::string& id = map_ids[sorted_menu_titles[i]]; |
| extension_items_.AppendExtensionItems(id, printable_selection_text, |
| &index); |
| } |
| |
| UMA_HISTOGRAM_TIMES("Extensions.ContextMenus_BuildTime", |
| base::TimeTicks::Now() - begin); |
| UMA_HISTOGRAM_COUNTS("Extensions.ContextMenus_ItemCount", index); |
| } |
| |
| void RenderViewContextMenu::InitMenu() { |
| if (chrome::IsRunningInForcedAppMode()) { |
| AppendAppModeItems(); |
| return; |
| } |
| |
| extensions::ViewType view_type = |
| extensions::GetViewType(source_web_contents_); |
| if (view_type == extensions::VIEW_TYPE_APP_SHELL) { |
| AppendPlatformAppItems(); |
| return; |
| } else if (view_type == extensions::VIEW_TYPE_EXTENSION_POPUP) { |
| AppendPopupExtensionItems(); |
| return; |
| } else if (view_type == extensions::VIEW_TYPE_PANEL) { |
| AppendPanelItems(); |
| return; |
| } |
| |
| const bool has_link = !params_.unfiltered_link_url.is_empty(); |
| const bool has_selection = !params_.selection_text.empty(); |
| |
| if (AppendCustomItems()) { |
| // If there's a selection, don't early return when there are custom items, |
| // but fall through to adding the normal ones after the custom ones. |
| if (has_selection) { |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| } else { |
| // Don't add items for Pepper menu. |
| if (!params_.custom_context.is_pepper_menu) |
| AppendDeveloperItems(); |
| return; |
| } |
| } |
| |
| // When no special node or text is selected and selection has no link, |
| // show page items. |
| if (params_.media_type == WebContextMenuData::MediaTypeNone && |
| !has_link && |
| !params_.is_editable && |
| !is_guest_ && |
| !has_selection) { |
| if (!params_.page_url.is_empty()) { |
| bool is_devtools = IsDevToolsURL(params_.page_url); |
| if (!is_devtools && !IsInternalResourcesURL(params_.page_url)) { |
| AppendPageItems(); |
| // Merge in frame items if we clicked within a frame that needs them. |
| if (!params_.frame_url.is_empty()) { |
| is_devtools = IsDevToolsURL(params_.frame_url); |
| if (!is_devtools && !IsInternalResourcesURL(params_.frame_url)) { |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| AppendFrameItems(); |
| } |
| } |
| } |
| } else { |
| DCHECK(params_.frame_url.is_empty()); |
| } |
| } |
| |
| // Do not show link related items for guest. |
| if (has_link && !is_guest_) { |
| AppendLinkItems(); |
| if (params_.media_type != WebContextMenuData::MediaTypeNone) |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| } |
| |
| switch (params_.media_type) { |
| case WebContextMenuData::MediaTypeNone: |
| break; |
| case WebContextMenuData::MediaTypeImage: |
| AppendImageItems(); |
| break; |
| case WebContextMenuData::MediaTypeVideo: |
| AppendVideoItems(); |
| break; |
| case WebContextMenuData::MediaTypeAudio: |
| AppendAudioItems(); |
| break; |
| case WebContextMenuData::MediaTypePlugin: |
| AppendPluginItems(); |
| break; |
| #ifdef WEBCONTEXT_MEDIATYPEFILE_DEFINED |
| case WebContextMenuData::MediaTypeFile: |
| break; |
| #endif |
| } |
| |
| if (params_.is_editable) |
| AppendEditableItems(); |
| else if (has_selection) |
| AppendCopyItem(); |
| |
| if (!is_guest_ && has_selection) { |
| AppendSearchProvider(); |
| if (!IsDevToolsURL(params_.page_url)) |
| AppendPrintItem(); |
| } |
| |
| if (!IsDevToolsURL(params_.page_url)) |
| AppendAllExtensionItems(); |
| |
| AppendDeveloperItems(); |
| |
| if (!is_guest_) { |
| if (!print_preview_menu_observer_.get()) { |
| print_preview_menu_observer_.reset( |
| new PrintPreviewContextMenuObserver(source_web_contents_)); |
| } |
| |
| observers_.AddObserver(print_preview_menu_observer_.get()); |
| } |
| } |
| |
| const Extension* RenderViewContextMenu::GetExtension() const { |
| extensions::ExtensionSystem* system = |
| extensions::ExtensionSystem::Get(profile_); |
| // There is no process manager in some tests. |
| if (!system->process_manager()) |
| return NULL; |
| |
| return system->process_manager()->GetExtensionForRenderViewHost( |
| source_web_contents_->GetRenderViewHost()); |
| } |
| |
| void RenderViewContextMenu::AppendAppModeItems() { |
| const bool has_selection = !params_.selection_text.empty(); |
| |
| if (params_.is_editable) |
| AppendEditableItems(); |
| else if (has_selection) |
| AppendCopyItem(); |
| } |
| |
| void RenderViewContextMenu::AppendPlatformAppItems() { |
| const Extension* platform_app = GetExtension(); |
| |
| // The RVH might be for a process sandboxed from the extension. |
| if (!platform_app) |
| return; |
| |
| DCHECK(platform_app->is_platform_app()); |
| |
| const bool has_selection = !params_.selection_text.empty(); |
| |
| // Add undo/redo, cut/copy/paste etc for text fields. |
| if (params_.is_editable) |
| AppendEditableItems(); |
| else if (has_selection) |
| AppendCopyItem(); |
| |
| int index = 0; |
| extension_items_.AppendExtensionItems(platform_app->id(), |
| PrintableSelectionText(), &index); |
| |
| // Add dev tools for unpacked extensions. |
| if (extensions::Manifest::IsUnpackedLocation(platform_app->location()) || |
| CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDebugPackedApps)) { |
| // Add a separator if there are any items already in the menu. |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP, |
| IDS_CONTENT_CONTEXT_RELOAD_PACKAGED_APP); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP, |
| IDS_CONTENT_CONTEXT_RESTART_APP); |
| AppendDeveloperItems(); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE, |
| IDS_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE); |
| } |
| } |
| |
| void RenderViewContextMenu::AppendPopupExtensionItems() { |
| const bool has_selection = !params_.selection_text.empty(); |
| |
| if (params_.is_editable) |
| AppendEditableItems(); |
| else if (has_selection) |
| AppendCopyItem(); |
| |
| if (has_selection) |
| AppendSearchProvider(); |
| |
| AppendAllExtensionItems(); |
| AppendDeveloperItems(); |
| } |
| |
| void RenderViewContextMenu::AppendPanelItems() { |
| const Extension* extension = GetExtension(); |
| |
| bool has_selection = !params_.selection_text.empty(); |
| |
| // Checking link should take precedence before checking selection since on Mac |
| // right-clicking a link will also make it selected. |
| if (params_.unfiltered_link_url.is_valid()) |
| AppendLinkItems(); |
| |
| if (params_.is_editable) |
| AppendEditableItems(); |
| else if (has_selection) |
| AppendCopyItem(); |
| |
| // Only add extension items from this extension. |
| int index = 0; |
| extension_items_.AppendExtensionItems(extension->id(), |
| PrintableSelectionText(), &index); |
| } |
| |
| void RenderViewContextMenu::AddMenuItem(int command_id, |
| const string16& title) { |
| menu_model_.AddItem(command_id, title); |
| } |
| |
| void RenderViewContextMenu::AddCheckItem(int command_id, |
| const string16& title) { |
| menu_model_.AddCheckItem(command_id, title); |
| } |
| |
| void RenderViewContextMenu::AddSeparator() { |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| } |
| |
| void RenderViewContextMenu::AddSubMenu(int command_id, |
| const string16& label, |
| ui::MenuModel* model) { |
| menu_model_.AddSubMenu(command_id, label, model); |
| } |
| |
| void RenderViewContextMenu::UpdateMenuItem(int command_id, |
| bool enabled, |
| bool hidden, |
| const string16& label) { |
| // This function needs platform-specific implementation. |
| NOTIMPLEMENTED(); |
| } |
| |
| RenderViewHost* RenderViewContextMenu::GetRenderViewHost() const { |
| return source_web_contents_->GetRenderViewHost(); |
| } |
| |
| WebContents* RenderViewContextMenu::GetWebContents() const { |
| return source_web_contents_; |
| } |
| |
| Profile* RenderViewContextMenu::GetProfile() const { |
| return profile_; |
| } |
| |
| bool RenderViewContextMenu::AppendCustomItems() { |
| size_t total_items = 0; |
| AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this, |
| &menu_model_); |
| return total_items > 0; |
| } |
| |
| void RenderViewContextMenu::AppendDeveloperItems() { |
| // Show Inspect Element in DevTools itself only in case of the debug |
| // devtools build. |
| bool show_developer_items = !IsDevToolsURL(params_.page_url); |
| |
| #if defined(DEBUG_DEVTOOLS) |
| show_developer_items = true; |
| #endif |
| |
| if (!show_developer_items) |
| return; |
| |
| // In the DevTools popup menu, "developer items" is normally the only |
| // section, so omit the separator there. |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTELEMENT, |
| IDS_CONTENT_CONTEXT_INSPECTELEMENT); |
| } |
| |
| void RenderViewContextMenu::AppendLinkItems() { |
| if (!params_.link_url.is_empty()) { |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB, |
| IDS_CONTENT_CONTEXT_OPENLINKNEWTAB); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW, |
| IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW); |
| if (params_.link_url.is_valid()) { |
| AppendProtocolHandlerSubMenu(); |
| } |
| |
| if (!external_) { |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD, |
| IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD); |
| } |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVELINKAS, |
| IDS_CONTENT_CONTEXT_SAVELINKAS); |
| } |
| |
| menu_model_.AddItemWithStringId( |
| IDC_CONTENT_CONTEXT_COPYLINKLOCATION, |
| params_.link_url.SchemeIs(chrome::kMailToScheme) ? |
| IDS_CONTENT_CONTEXT_COPYEMAILADDRESS : |
| IDS_CONTENT_CONTEXT_COPYLINKLOCATION); |
| } |
| |
| void RenderViewContextMenu::AppendImageItems() { |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, |
| IDS_CONTENT_CONTEXT_SAVEIMAGEAS); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION, |
| IDS_CONTENT_CONTEXT_COPYIMAGELOCATION); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGE, |
| IDS_CONTENT_CONTEXT_COPYIMAGE); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB, |
| IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB); |
| } |
| |
| void RenderViewContextMenu::AppendAudioItems() { |
| AppendMediaItems(); |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, |
| IDS_CONTENT_CONTEXT_SAVEAUDIOAS); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, |
| IDS_CONTENT_CONTEXT_COPYAUDIOLOCATION); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, |
| IDS_CONTENT_CONTEXT_OPENAUDIONEWTAB); |
| } |
| |
| void RenderViewContextMenu::AppendVideoItems() { |
| AppendMediaItems(); |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, |
| IDS_CONTENT_CONTEXT_SAVEVIDEOAS); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, |
| IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, |
| IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB); |
| } |
| |
| void RenderViewContextMenu::AppendMediaItems() { |
| int media_flags = params_.media_flags; |
| |
| menu_model_.AddItemWithStringId( |
| IDC_CONTENT_CONTEXT_PLAYPAUSE, |
| media_flags & WebContextMenuData::MediaPaused ? |
| IDS_CONTENT_CONTEXT_PLAY : |
| IDS_CONTENT_CONTEXT_PAUSE); |
| |
| menu_model_.AddItemWithStringId( |
| IDC_CONTENT_CONTEXT_MUTE, |
| media_flags & WebContextMenuData::MediaMuted ? |
| IDS_CONTENT_CONTEXT_UNMUTE : |
| IDS_CONTENT_CONTEXT_MUTE); |
| |
| menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_LOOP, |
| IDS_CONTENT_CONTEXT_LOOP); |
| menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_CONTROLS, |
| IDS_CONTENT_CONTEXT_CONTROLS); |
| } |
| |
| void RenderViewContextMenu::AppendPluginItems() { |
| if (params_.page_url == params_.src_url) { |
| // Full page plugin, so show page menu items. |
| if (params_.link_url.is_empty() && params_.selection_text.empty()) |
| AppendPageItems(); |
| } else { |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, |
| IDS_CONTENT_CONTEXT_SAVEPAGEAS); |
| menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT); |
| } |
| |
| if (params_.media_flags & WebContextMenuData::MediaCanRotate) { |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECW, |
| IDS_CONTENT_CONTEXT_ROTATECW); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECCW, |
| IDS_CONTENT_CONTEXT_ROTATECCW); |
| } |
| } |
| |
| void RenderViewContextMenu::AppendPageItems() { |
| menu_model_.AddItemWithStringId(IDC_BACK, IDS_CONTENT_CONTEXT_BACK); |
| menu_model_.AddItemWithStringId(IDC_FORWARD, IDS_CONTENT_CONTEXT_FORWARD); |
| menu_model_.AddItemWithStringId(IDC_RELOAD, IDS_CONTENT_CONTEXT_RELOAD); |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| menu_model_.AddItemWithStringId(IDC_SAVE_PAGE, |
| IDS_CONTENT_CONTEXT_SAVEPAGEAS); |
| menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT); |
| |
| if (TranslateManager::IsTranslatableURL(params_.page_url)) { |
| std::string locale = g_browser_process->GetApplicationLocale(); |
| locale = TranslateManager::GetLanguageCode(locale); |
| string16 language = l10n_util::GetDisplayNameForLocale(locale, locale, |
| true); |
| menu_model_.AddItem( |
| IDC_CONTENT_CONTEXT_TRANSLATE, |
| l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE, language)); |
| } |
| |
| menu_model_.AddItemWithStringId(IDC_VIEW_SOURCE, |
| IDS_CONTENT_CONTEXT_VIEWPAGESOURCE); |
| // Only add View Page Info if there's a browser. This is a temporary thing |
| // while View Page Info crashes Chrome Frame; see http://crbug.com/120901. |
| // TODO(grt) Remove this once page info is back for Chrome Frame. |
| if (!external_) { |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWPAGEINFO, |
| IDS_CONTENT_CONTEXT_VIEWPAGEINFO); |
| } |
| } |
| |
| void RenderViewContextMenu::AppendFrameItems() { |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOADFRAME, |
| IDS_CONTENT_CONTEXT_RELOADFRAME); |
| // These two menu items have yet to be implemented. |
| // http://code.google.com/p/chromium/issues/detail?id=11827 |
| // IDS_CONTENT_CONTEXT_SAVEFRAMEAS |
| // IDS_CONTENT_CONTEXT_PRINTFRAME |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE, |
| IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE); |
| // Only add View Frame Info if there's a browser. This is a temporary thing |
| // while View Frame Info crashes Chrome Frame; see http://crbug.com/120901. |
| // TODO(grt) Remove this once frame info is back for Chrome Frame. |
| if (!external_) { |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMEINFO, |
| IDS_CONTENT_CONTEXT_VIEWFRAMEINFO); |
| } |
| } |
| |
| void RenderViewContextMenu::AppendCopyItem() { |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY, |
| IDS_CONTENT_CONTEXT_COPY); |
| } |
| |
| void RenderViewContextMenu::AppendPrintItem() { |
| if (profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) && |
| (params_.media_type == WebContextMenuData::MediaTypeNone || |
| params_.media_flags & WebContextMenuData::MediaCanPrint)) { |
| menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT); |
| } |
| } |
| |
| void RenderViewContextMenu::AppendSearchProvider() { |
| DCHECK(profile_); |
| |
| TrimWhitespace(params_.selection_text, TRIM_ALL, ¶ms_.selection_text); |
| if (params_.selection_text.empty()) |
| return; |
| |
| ReplaceChars(params_.selection_text, AutocompleteMatch::kInvalidChars, |
| ASCIIToUTF16(" "), ¶ms_.selection_text); |
| |
| AutocompleteMatch match; |
| AutocompleteClassifierFactory::GetForProfile(profile_)->Classify( |
| params_.selection_text, false, false, &match, NULL); |
| selection_navigation_url_ = match.destination_url; |
| if (!selection_navigation_url_.is_valid()) |
| return; |
| |
| string16 printable_selection_text = PrintableSelectionText(); |
| EscapeAmpersands(&printable_selection_text); |
| |
| if (AutocompleteMatch::IsSearchType(match.type)) { |
| const TemplateURL* const default_provider = |
| TemplateURLServiceFactory::GetForProfile(profile_)-> |
| GetDefaultSearchProvider(); |
| if (!default_provider) |
| return; |
| menu_model_.AddItem( |
| IDC_CONTENT_CONTEXT_SEARCHWEBFOR, |
| l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR, |
| default_provider->short_name(), |
| printable_selection_text)); |
| } else { |
| if ((selection_navigation_url_ != params_.link_url) && |
| ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme( |
| selection_navigation_url_.scheme())) { |
| menu_model_.AddItem( |
| IDC_CONTENT_CONTEXT_GOTOURL, |
| l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_GOTOURL, |
| printable_selection_text)); |
| } |
| } |
| } |
| |
| void RenderViewContextMenu::AppendEditableItems() { |
| const bool use_spellcheck_and_search = !chrome::IsRunningInForcedAppMode(); |
| |
| if (use_spellcheck_and_search) |
| AppendSpellingSuggestionsSubMenu(); |
| |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO, |
| IDS_CONTENT_CONTEXT_UNDO); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO, |
| IDS_CONTENT_CONTEXT_REDO); |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_CUT, |
| IDS_CONTENT_CONTEXT_CUT); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY, |
| IDS_CONTENT_CONTEXT_COPY); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE, |
| IDS_CONTENT_CONTEXT_PASTE); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE, |
| IDS_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_DELETE, |
| IDS_CONTENT_CONTEXT_DELETE); |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| |
| if (use_spellcheck_and_search && !params_.keyword_url.is_empty()) { |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ADDSEARCHENGINE, |
| IDS_CONTENT_CONTEXT_ADDSEARCHENGINE); |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| } |
| |
| if (use_spellcheck_and_search) |
| AppendSpellcheckOptionsSubMenu(); |
| AppendSpeechInputOptionsSubMenu(); |
| AppendPlatformEditableItems(); |
| |
| menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SELECTALL, |
| IDS_CONTENT_CONTEXT_SELECTALL); |
| } |
| |
| void RenderViewContextMenu::AppendSpellingSuggestionsSubMenu() { |
| if (!spelling_menu_observer_.get()) |
| spelling_menu_observer_.reset(new SpellingMenuObserver(this)); |
| observers_.AddObserver(spelling_menu_observer_.get()); |
| spelling_menu_observer_->InitMenu(params_); |
| } |
| |
| void RenderViewContextMenu::AppendSpellcheckOptionsSubMenu() { |
| if (!spellchecker_submenu_observer_.get()) { |
| spellchecker_submenu_observer_.reset(new SpellCheckerSubMenuObserver( |
| this, this, kSpellcheckRadioGroup)); |
| } |
| spellchecker_submenu_observer_->InitMenu(params_); |
| observers_.AddObserver(spellchecker_submenu_observer_.get()); |
| } |
| |
| void RenderViewContextMenu::AppendSpeechInputOptionsSubMenu() { |
| if (params_.speech_input_enabled) { |
| speech_input_submenu_model_.AddCheckItem( |
| IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES, |
| l10n_util::GetStringUTF16( |
| IDS_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES)); |
| |
| speech_input_submenu_model_.AddItemWithStringId( |
| IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT, |
| IDS_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT); |
| |
| menu_model_.AddSubMenu( |
| IDC_SPEECH_INPUT_MENU, |
| l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPEECH_INPUT_MENU), |
| &speech_input_submenu_model_); |
| } |
| } |
| |
| void RenderViewContextMenu::AppendProtocolHandlerSubMenu() { |
| const ProtocolHandlerRegistry::ProtocolHandlerList handlers = |
| GetHandlersForLinkUrl(); |
| if (handlers.empty()) |
| return; |
| size_t max = IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST - |
| IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST; |
| for (size_t i = 0; i < handlers.size() && i <= max; i++) { |
| protocol_handler_submenu_model_.AddItem( |
| IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST + i, |
| handlers[i].title()); |
| } |
| protocol_handler_submenu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
| protocol_handler_submenu_model_.AddItem( |
| IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS, |
| l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH_CONFIGURE)); |
| |
| menu_model_.AddSubMenu( |
| IDC_CONTENT_CONTEXT_OPENLINKWITH, |
| l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH), |
| &protocol_handler_submenu_model_); |
| } |
| |
| void RenderViewContextMenu::AppendPlatformEditableItems() { |
| } |
| |
| // Menu delegate functions ----------------------------------------------------- |
| |
| bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { |
| // If this command is is added by one of our observers, we dispatch it to the |
| // observer. |
| ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); |
| RenderViewContextMenuObserver* observer; |
| while ((observer = it.GetNext()) != NULL) { |
| if (observer->IsCommandIdSupported(id)) |
| return observer->IsCommandIdEnabled(id); |
| } |
| |
| CoreTabHelper* core_tab_helper = |
| CoreTabHelper::FromWebContents(source_web_contents_); |
| int content_restrictions = 0; |
| if (core_tab_helper) |
| content_restrictions = core_tab_helper->content_restrictions(); |
| if (id == IDC_PRINT && |
| (content_restrictions & CONTENT_RESTRICTION_PRINT)) { |
| return false; |
| } |
| |
| if (id == IDC_SAVE_PAGE && |
| (content_restrictions & CONTENT_RESTRICTION_SAVE)) { |
| return false; |
| } |
| |
| // Allow Spell Check language items on sub menu for text area context menu. |
| if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) && |
| (id < IDC_SPELLCHECK_LANGUAGES_LAST)) { |
| return profile_->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck); |
| } |
| |
| // Custom items. |
| if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { |
| return IsCustomItemEnabled(params_.custom_items, id); |
| } |
| |
| // Extension items. |
| if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { |
| return extension_items_.IsCommandIdEnabled(id); |
| } |
| |
| if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST && |
| id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) { |
| return true; |
| } |
| |
| IncognitoModePrefs::Availability incognito_avail = |
| IncognitoModePrefs::GetAvailability(profile_->GetPrefs()); |
| switch (id) { |
| case IDC_BACK: |
| return source_web_contents_->GetController().CanGoBack(); |
| |
| case IDC_FORWARD: |
| return source_web_contents_->GetController().CanGoForward(); |
| |
| case IDC_RELOAD: { |
| CoreTabHelper* core_tab_helper = |
| CoreTabHelper::FromWebContents(source_web_contents_); |
| if (!core_tab_helper) |
| return false; |
| |
| CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate(); |
| return !core_delegate || |
| core_delegate->CanReloadContents(source_web_contents_); |
| } |
| |
| case IDC_VIEW_SOURCE: |
| case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE: |
| return source_web_contents_->GetController().CanViewSource(); |
| |
| case IDC_CONTENT_CONTEXT_INSPECTELEMENT: |
| case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE: |
| case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP: |
| case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP: |
| return IsDevCommandEnabled(id); |
| |
| case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: |
| if (source_web_contents_->GetController().GetActiveEntry() == NULL) |
| return false; |
| // Disabled if no browser is associated (e.g. desktop notifications). |
| if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL) |
| return false; |
| return true; |
| |
| case IDC_CONTENT_CONTEXT_TRANSLATE: { |
| TranslateTabHelper* translate_tab_helper = |
| TranslateTabHelper::FromWebContents(source_web_contents_); |
| if (!translate_tab_helper) |
| return false; |
| std::string original_lang = |
| translate_tab_helper->language_state().original_language(); |
| std::string target_lang = g_browser_process->GetApplicationLocale(); |
| target_lang = TranslateManager::GetLanguageCode(target_lang); |
| // Note that we intentionally enable the menu even if the original and |
| // target languages are identical. This is to give a way to user to |
| // translate a page that might contains text fragments in a different |
| // language. |
| return ((params_.edit_flags & WebContextMenuData::CanTranslate) != 0) && |
| !original_lang.empty() && // Did we receive the page language yet? |
| !translate_tab_helper->language_state().IsPageTranslated() && |
| !source_web_contents_->GetInterstitialPage() && |
| // There are some application locales which can't be used as a |
| // target language for translation. |
| TranslateManager::IsSupportedLanguage(target_lang) && |
| // Disable on the Instant Extended NTP. |
| !chrome::IsInstantNTP(source_web_contents_); |
| } |
| |
| case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: |
| case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW: |
| return params_.link_url.is_valid(); |
| |
| case IDC_CONTENT_CONTEXT_COPYLINKLOCATION: |
| return params_.unfiltered_link_url.is_valid(); |
| |
| case IDC_CONTENT_CONTEXT_SAVELINKAS: { |
| PrefService* local_state = g_browser_process->local_state(); |
| DCHECK(local_state); |
| // Test if file-selection dialogs are forbidden by policy. |
| if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs)) |
| return false; |
| |
| return params_.link_url.is_valid() && |
| ProfileIOData::IsHandledProtocol(params_.link_url.scheme()); |
| } |
| |
| case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: { |
| PrefService* local_state = g_browser_process->local_state(); |
| DCHECK(local_state); |
| // Test if file-selection dialogs are forbidden by policy. |
| if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs)) |
| return false; |
| |
| return params_.src_url.is_valid() && |
| ProfileIOData::IsHandledProtocol(params_.src_url.scheme()); |
| } |
| |
| case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB: |
| // The images shown in the most visited thumbnails do not currently open |
| // in a new tab as they should. Disabling this context menu option for |
| // now, as a quick hack, before we resolve this issue (Issue = 2608). |
| // TODO(sidchat): Enable this option once this issue is resolved. |
| if (params_.src_url.scheme() == chrome::kChromeUIScheme || |
| !params_.src_url.is_valid()) |
| return false; |
| return true; |
| |
| case IDC_CONTENT_CONTEXT_COPYIMAGE: |
| return !params_.is_image_blocked; |
| |
| // Media control commands should all be disabled if the player is in an |
| // error state. |
| case IDC_CONTENT_CONTEXT_PLAYPAUSE: |
| case IDC_CONTENT_CONTEXT_LOOP: |
| return (params_.media_flags & |
| WebContextMenuData::MediaInError) == 0; |
| |
| // Mute and unmute should also be disabled if the player has no audio. |
| case IDC_CONTENT_CONTEXT_MUTE: |
| return (params_.media_flags & |
| WebContextMenuData::MediaHasAudio) != 0 && |
| (params_.media_flags & |
| WebContextMenuData::MediaInError) == 0; |
| |
| // Media controls can be toggled only for video player. If we toggle |
| // controls for audio then the player disappears, and there is no way to |
| // return it back. |
| case IDC_CONTENT_CONTEXT_CONTROLS: |
| return (params_.media_flags & |
| WebContextMenuData::MediaHasVideo) != 0; |
| |
| case IDC_CONTENT_CONTEXT_ROTATECW: |
| case IDC_CONTENT_CONTEXT_ROTATECCW: |
| return |
| (params_.media_flags & WebContextMenuData::MediaCanRotate) != 0; |
| |
| case IDC_CONTENT_CONTEXT_COPYAVLOCATION: |
| case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION: |
| return params_.src_url.is_valid(); |
| |
| case IDC_CONTENT_CONTEXT_SAVEAVAS: { |
| PrefService* local_state = g_browser_process->local_state(); |
| DCHECK(local_state); |
| // Test if file-selection dialogs are forbidden by policy. |
| if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs)) |
| return false; |
| |
| const GURL& url = params_.src_url; |
| return (params_.media_flags & WebContextMenuData::MediaCanSave) && |
| url.is_valid() && ProfileIOData::IsHandledProtocol(url.scheme()) && |
| // Do not save the preview PDF on the print preview page. |
| !(printing::PrintPreviewDialogController::IsPrintPreviewURL(url)); |
| } |
| |
| case IDC_CONTENT_CONTEXT_OPENAVNEWTAB: |
| return true; |
| |
| case IDC_SAVE_PAGE: { |
| CoreTabHelper* core_tab_helper = |
| CoreTabHelper::FromWebContents(source_web_contents_); |
| if (!core_tab_helper) |
| return false; |
| |
| CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate(); |
| if (core_delegate && |
| !core_delegate->CanSaveContents(source_web_contents_)) |
| return false; |
| |
| PrefService* local_state = g_browser_process->local_state(); |
| DCHECK(local_state); |
| // Test if file-selection dialogs are forbidden by policy. |
| if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs)) |
| return false; |
| |
| // Instead of using GetURL here, we use url() (which is the "real" url of |
| // the page) from the NavigationEntry because its reflects their origin |
| // rather than the display one (returned by GetURL) which may be |
| // different (like having "view-source:" on the front). |
| NavigationEntry* active_entry = |
| source_web_contents_->GetController().GetActiveEntry(); |
| return content::IsSavableURL( |
| (active_entry) ? active_entry->GetURL() : GURL()); |
| } |
| |
| case IDC_CONTENT_CONTEXT_RELOADFRAME: |
| return params_.frame_url.is_valid(); |
| |
| case IDC_CONTENT_CONTEXT_UNDO: |
| return !!(params_.edit_flags & WebContextMenuData::CanUndo); |
| |
| case IDC_CONTENT_CONTEXT_REDO: |
| return !!(params_.edit_flags & WebContextMenuData::CanRedo); |
| |
| case IDC_CONTENT_CONTEXT_CUT: |
| return !!(params_.edit_flags & WebContextMenuData::CanCut); |
| |
| case IDC_CONTENT_CONTEXT_COPY: |
| return !!(params_.edit_flags & WebContextMenuData::CanCopy); |
| |
| case IDC_CONTENT_CONTEXT_PASTE: |
| case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE: |
| return !!(params_.edit_flags & WebContextMenuData::CanPaste); |
| |
| case IDC_CONTENT_CONTEXT_DELETE: |
| return !!(params_.edit_flags & WebContextMenuData::CanDelete); |
| |
| case IDC_CONTENT_CONTEXT_SELECTALL: |
| return !!(params_.edit_flags & WebContextMenuData::CanSelectAll); |
| |
| case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD: |
| return !profile_->IsOffTheRecord() && params_.link_url.is_valid() && |
| incognito_avail != IncognitoModePrefs::DISABLED; |
| |
| case IDC_PRINT: |
| return profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) && |
| (params_.media_type == WebContextMenuData::MediaTypeNone || |
| params_.media_flags & WebContextMenuData::MediaCanPrint); |
| |
| case IDC_CONTENT_CONTEXT_SEARCHWEBFOR: |
| case IDC_CONTENT_CONTEXT_GOTOURL: |
| case IDC_SPELLPANEL_TOGGLE: |
| case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS: |
| return true; |
| case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO: |
| // Disabled if no browser is associated (e.g. desktop notifications). |
| if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL) |
| return false; |
| return true; |
| |
| case IDC_CHECK_SPELLING_WHILE_TYPING: |
| return profile_->GetPrefs()->GetBoolean( |
| prefs::kEnableContinuousSpellcheck); |
| |
| #if !defined(OS_MACOSX) && defined(OS_POSIX) |
| // TODO(suzhe): this should not be enabled for password fields. |
| case IDC_INPUT_METHODS_MENU: |
| return true; |
| #endif |
| |
| case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE: |
| return !params_.keyword_url.is_empty(); |
| |
| case IDC_SPELLCHECK_MENU: |
| return true; |
| |
| case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES: |
| case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT: |
| case IDC_SPEECH_INPUT_MENU: |
| return true; |
| |
| case IDC_CONTENT_CONTEXT_OPENLINKWITH: |
| return true; |
| |
| case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS: |
| return true; |
| |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| bool RenderViewContextMenu::IsCommandIdChecked(int id) const { |
| // If this command is is added by one of our observers, we dispatch it to the |
| // observer. |
| ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); |
| RenderViewContextMenuObserver* observer; |
| while ((observer = it.GetNext()) != NULL) { |
| if (observer->IsCommandIdSupported(id)) |
| return observer->IsCommandIdChecked(id); |
| } |
| |
| // See if the video is set to looping. |
| if (id == IDC_CONTENT_CONTEXT_LOOP) { |
| return (params_.media_flags & |
| WebContextMenuData::MediaLoop) != 0; |
| } |
| |
| if (id == IDC_CONTENT_CONTEXT_CONTROLS) { |
| return (params_.media_flags & |
| WebContextMenuData::MediaControls) != 0; |
| } |
| |
| // Custom items. |
| if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { |
| return IsCustomItemChecked(params_.custom_items, id); |
| } |
| |
| // Extension items. |
| if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { |
| return extension_items_.IsCommandIdChecked(id); |
| } |
| |
| #if defined(ENABLE_INPUT_SPEECH) |
| // Check box for menu item 'Block offensive words'. |
| if (id == IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES) { |
| return profile_->GetPrefs()->GetBoolean( |
| prefs::kSpeechRecognitionFilterProfanities); |
| } |
| #endif |
| |
| return false; |
| } |
| |
| void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) { |
| command_executed_ = true; |
| // If this command is is added by one of our observers, we dispatch it to the |
| // observer. |
| ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); |
| RenderViewContextMenuObserver* observer; |
| while ((observer = it.GetNext()) != NULL) { |
| if (observer->IsCommandIdSupported(id)) |
| return observer->ExecuteCommand(id); |
| } |
| |
| RecordUsedItem(id); |
| |
| RenderViewHost* rvh = source_web_contents_->GetRenderViewHost(); |
| |
| // Process custom actions range. |
| if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { |
| unsigned action = id - IDC_CONTENT_CONTEXT_CUSTOM_FIRST; |
| const content::CustomContextMenuContext& context = params_.custom_context; |
| #if defined(ENABLE_PLUGINS) |
| if (context.request_id && !context.is_pepper_menu) { |
| ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins( |
| rvh->GetProcess()->GetID()); |
| } |
| #endif |
| rvh->ExecuteCustomContextMenuCommand(action, context); |
| return; |
| } |
| |
| // Process extension menu items. |
| if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && |
| id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { |
| extension_items_.ExecuteCommand(id, source_web_contents_, params_); |
| return; |
| } |
| |
| if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST && |
| id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) { |
| ProtocolHandlerRegistry::ProtocolHandlerList handlers = |
| GetHandlersForLinkUrl(); |
| if (handlers.empty()) { |
| return; |
| } |
| content::RecordAction( |
| UserMetricsAction("RegisterProtocolHandler.ContextMenu_Open")); |
| int handlerIndex = id - IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST; |
| WindowOpenDisposition disposition = |
| ForceNewTabDispositionFromEventFlags(event_flags); |
| OpenURL( |
| handlers[handlerIndex].TranslateUrl(params_.link_url), |
| params_.frame_url.is_empty() ? params_.page_url : params_.frame_url, |
| params_.frame_id, |
| disposition, |
| content::PAGE_TRANSITION_LINK); |
| return; |
| } |
| |
| switch (id) { |
| case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: { |
| Browser* browser = |
| chrome::FindBrowserWithWebContents(source_web_contents_); |
| OpenURL( |
| params_.link_url, |
| params_.frame_url.is_empty() ? params_.page_url : params_.frame_url, |
| params_.frame_id, |
| !browser || browser->is_app() ? |
| NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB, |
| content::PAGE_TRANSITION_LINK); |
| break; |
| } |
| case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW: |
| OpenURL( |
| params_.link_url, |
| params_.frame_url.is_empty() ? params_.page_url : params_.frame_url, |
| params_.frame_id, |
| NEW_WINDOW, content::PAGE_TRANSITION_LINK); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD: |
| OpenURL(params_.link_url, |
| GURL(), |
| params_.frame_id, |
| OFF_THE_RECORD, |
| content::PAGE_TRANSITION_LINK); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_SAVELINKAS: { |
| download_util::RecordDownloadSource( |
| download_util::INITIATED_BY_CONTEXT_MENU); |
| const GURL& referrer = |
| params_.frame_url.is_empty() ? params_.page_url : params_.frame_url; |
| const GURL& url = params_.link_url; |
| DownloadManager* dlm = BrowserContext::GetDownloadManager(profile_); |
| scoped_ptr<DownloadUrlParameters> dl_params( |
| DownloadUrlParameters::FromWebContents(source_web_contents_, url)); |
| dl_params->set_referrer( |
| content::Referrer(referrer, params_.referrer_policy)); |
| dl_params->set_referrer_encoding(params_.frame_charset); |
| dl_params->set_prompt(true); |
| dlm->DownloadUrl(dl_params.Pass()); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_SAVEAVAS: |
| case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: { |
| download_util::RecordDownloadSource( |
| download_util::INITIATED_BY_CONTEXT_MENU); |
| const GURL& referrer = |
| params_.frame_url.is_empty() ? params_.page_url : params_.frame_url; |
| const GURL& url = params_.src_url; |
| int64 post_id = -1; |
| if (url == source_web_contents_->GetURL()) { |
| const NavigationEntry* entry = |
| source_web_contents_->GetController().GetActiveEntry(); |
| if (entry) |
| post_id = entry->GetPostID(); |
| } |
| DownloadManager* dlm = BrowserContext::GetDownloadManager(profile_); |
| scoped_ptr<DownloadUrlParameters> dl_params( |
| DownloadUrlParameters::FromWebContents(source_web_contents_, url)); |
| dl_params->set_referrer( |
| content::Referrer(referrer, params_.referrer_policy)); |
| dl_params->set_post_id(post_id); |
| dl_params->set_prefer_cache(true); |
| if (post_id >= 0) |
| dl_params->set_method("POST"); |
| dl_params->set_prompt(true); |
| dlm->DownloadUrl(dl_params.Pass()); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_COPYLINKLOCATION: |
| WriteURLToClipboard(params_.unfiltered_link_url); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION: |
| case IDC_CONTENT_CONTEXT_COPYAVLOCATION: |
| WriteURLToClipboard(params_.src_url); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_COPYIMAGE: |
| CopyImageAt(params_.x, params_.y); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB: |
| case IDC_CONTENT_CONTEXT_OPENAVNEWTAB: |
| OpenURL( |
| params_.src_url, |
| params_.frame_url.is_empty() ? params_.page_url : params_.frame_url, |
| params_.frame_id, |
| NEW_BACKGROUND_TAB, content::PAGE_TRANSITION_LINK); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_PLAYPAUSE: { |
| bool play = !!(params_.media_flags & WebContextMenuData::MediaPaused); |
| if (play) { |
| content::RecordAction(UserMetricsAction("MediaContextMenu_Play")); |
| } else { |
| content::RecordAction(UserMetricsAction("MediaContextMenu_Pause")); |
| } |
| MediaPlayerActionAt(gfx::Point(params_.x, params_.y), |
| WebMediaPlayerAction( |
| WebMediaPlayerAction::Play, play)); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_MUTE: { |
| bool mute = !(params_.media_flags & WebContextMenuData::MediaMuted); |
| if (mute) { |
| content::RecordAction(UserMetricsAction("MediaContextMenu_Mute")); |
| } else { |
| content::RecordAction(UserMetricsAction("MediaContextMenu_Unmute")); |
| } |
| MediaPlayerActionAt(gfx::Point(params_.x, params_.y), |
| WebMediaPlayerAction( |
| WebMediaPlayerAction::Mute, mute)); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_LOOP: |
| content::RecordAction(UserMetricsAction("MediaContextMenu_Loop")); |
| MediaPlayerActionAt(gfx::Point(params_.x, params_.y), |
| WebMediaPlayerAction( |
| WebMediaPlayerAction::Loop, |
| !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP))); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_CONTROLS: |
| content::RecordAction(UserMetricsAction("MediaContextMenu_Controls")); |
| MediaPlayerActionAt( |
| gfx::Point(params_.x, params_.y), |
| WebMediaPlayerAction( |
| WebMediaPlayerAction::Controls, |
| !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS))); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_ROTATECW: |
| content::RecordAction( |
| UserMetricsAction("PluginContextMenu_RotateClockwise")); |
| PluginActionAt( |
| gfx::Point(params_.x, params_.y), |
| WebPluginAction( |
| WebPluginAction::Rotate90Clockwise, |
| true)); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_ROTATECCW: |
| content::RecordAction( |
| UserMetricsAction("PluginContextMenu_RotateCounterclockwise")); |
| PluginActionAt( |
| gfx::Point(params_.x, params_.y), |
| WebPluginAction( |
| WebPluginAction::Rotate90Counterclockwise, |
| true)); |
| break; |
| |
| case IDC_BACK: |
| source_web_contents_->GetController().GoBack(); |
| break; |
| |
| case IDC_FORWARD: |
| source_web_contents_->GetController().GoForward(); |
| break; |
| |
| case IDC_SAVE_PAGE: |
| source_web_contents_->OnSavePage(); |
| break; |
| |
| case IDC_RELOAD: |
| // Prevent the modal "Resubmit form post" dialog from appearing in the |
| // context of an external context menu. |
| source_web_contents_->GetController().Reload(!external_); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP: { |
| const Extension* platform_app = GetExtension(); |
| DCHECK(platform_app); |
| DCHECK(platform_app->is_platform_app()); |
| |
| extensions::ExtensionSystem::Get(profile_)->extension_service()-> |
| ReloadExtension(platform_app->id()); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP: { |
| const Extension* platform_app = GetExtension(); |
| DCHECK(platform_app); |
| DCHECK(platform_app->is_platform_app()); |
| |
| apps::AppLoadService::Get(profile_)->RestartApplication( |
| platform_app->id()); |
| break; |
| } |
| |
| case IDC_PRINT: |
| if (params_.media_type == WebContextMenuData::MediaTypeNone) { |
| printing::PrintViewManager* print_view_manager = |
| printing::PrintViewManager::FromWebContents(source_web_contents_); |
| if (!print_view_manager) |
| break; |
| if (profile_->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled)) { |
| print_view_manager->PrintNow(); |
| } else { |
| print_view_manager->PrintPreviewNow(!params_.selection_text.empty()); |
| } |
| } else { |
| rvh->Send(new PrintMsg_PrintNodeUnderContextMenu(rvh->GetRoutingID())); |
| } |
| break; |
| |
| case IDC_VIEW_SOURCE: |
| source_web_contents_->ViewSource(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_INSPECTELEMENT: |
| Inspect(params_.x, params_.y); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE: { |
| const Extension* platform_app = GetExtension(); |
| DCHECK(platform_app); |
| DCHECK(platform_app->is_platform_app()); |
| |
| extensions::ExtensionSystem::Get(profile_)->extension_service()-> |
| InspectBackgroundPage(platform_app); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: { |
| NavigationController* controller = &source_web_contents_->GetController(); |
| NavigationEntry* nav_entry = controller->GetActiveEntry(); |
| Browser* browser = |
| chrome::FindBrowserWithWebContents(source_web_contents_); |
| chrome::ShowWebsiteSettings(browser, source_web_contents_, |
| nav_entry->GetURL(), nav_entry->GetSSL()); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_TRANSLATE: { |
| // A translation might have been triggered by the time the menu got |
| // selected, do nothing in that case. |
| TranslateTabHelper* translate_tab_helper = |
| TranslateTabHelper::FromWebContents(source_web_contents_); |
| if (!translate_tab_helper || |
| translate_tab_helper->language_state().IsPageTranslated() || |
| translate_tab_helper->language_state().translation_pending()) { |
| return; |
| } |
| std::string original_lang = |
| translate_tab_helper->language_state().original_language(); |
| std::string target_lang = g_browser_process->GetApplicationLocale(); |
| target_lang = TranslateManager::GetLanguageCode(target_lang); |
| // Since the user decided to translate for that language and site, clears |
| // any preferences for not translating them. |
| TranslatePrefs prefs(profile_->GetPrefs()); |
| prefs.UnblockLanguage(original_lang); |
| prefs.RemoveSiteFromBlacklist(params_.page_url.HostNoBrackets()); |
| TranslateManager::GetInstance()->TranslatePage( |
| source_web_contents_, original_lang, target_lang); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_RELOADFRAME: |
| rvh->ReloadFrame(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE: |
| source_web_contents_->ViewFrameSource(params_.frame_url, |
| params_.frame_page_state); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO: { |
| Browser* browser = chrome::FindBrowserWithWebContents( |
| source_web_contents_); |
| chrome::ShowWebsiteSettings(browser, source_web_contents_, |
| params_.frame_url, params_.security_info); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_UNDO: |
| rvh->Undo(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_REDO: |
| rvh->Redo(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_CUT: |
| rvh->Cut(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_COPY: |
| rvh->Copy(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_PASTE: |
| rvh->Paste(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE: |
| rvh->PasteAndMatchStyle(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_DELETE: |
| rvh->Delete(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_SELECTALL: |
| rvh->SelectAll(); |
| break; |
| |
| case IDC_CONTENT_CONTEXT_SEARCHWEBFOR: |
| case IDC_CONTENT_CONTEXT_GOTOURL: { |
| WindowOpenDisposition disposition = |
| ForceNewTabDispositionFromEventFlags(event_flags); |
| OpenURL(selection_navigation_url_, |
| GURL(), |
| params_.frame_id, |
| disposition, |
| content::PAGE_TRANSITION_LINK); |
| break; |
| } |
| case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS: { |
| WindowOpenDisposition disposition = |
| ForceNewTabDispositionFromEventFlags(event_flags); |
| std::string url = std::string(chrome::kChromeUISettingsURL) + |
| chrome::kLanguageOptionsSubPage; |
| OpenURL(GURL(url), GURL(), 0, disposition, content::PAGE_TRANSITION_LINK); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS: { |
| content::RecordAction( |
| UserMetricsAction("RegisterProtocolHandler.ContextMenu_Settings")); |
| WindowOpenDisposition disposition = |
| ForceNewTabDispositionFromEventFlags(event_flags); |
| std::string url = std::string(chrome::kChromeUISettingsURL) + |
| chrome::kHandlerSettingsSubPage; |
| OpenURL(GURL(url), GURL(), 0, disposition, content::PAGE_TRANSITION_LINK); |
| break; |
| } |
| |
| case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE: { |
| // Make sure the model is loaded. |
| TemplateURLService* model = |
| TemplateURLServiceFactory::GetForProfile(profile_); |
| if (!model) |
| return; |
| model->Load(); |
| |
| SearchEngineTabHelper* search_engine_tab_helper = |
| SearchEngineTabHelper::FromWebContents(source_web_contents_); |
| if (search_engine_tab_helper && |
| search_engine_tab_helper->delegate()) { |
| string16 keyword(TemplateURLService::GenerateKeyword(params_.page_url)); |
| TemplateURLData data; |
| data.short_name = keyword; |
| data.SetKeyword(keyword); |
| data.SetURL(params_.keyword_url.spec()); |
| data.favicon_url = |
| TemplateURL::GenerateFaviconURL(params_.page_url.GetOrigin()); |
| // Takes ownership of the TemplateURL. |
| search_engine_tab_helper->delegate()-> |
| ConfirmAddSearchProvider(new TemplateURL(profile_, data), profile_); |
| } |
| break; |
| } |
| |
| #if defined(ENABLE_INPUT_SPEECH) |
| case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES: { |
| profile_->GetPrefs()->SetBoolean( |
| prefs::kSpeechRecognitionFilterProfanities, |
| !profile_->GetPrefs()->GetBoolean( |
| prefs::kSpeechRecognitionFilterProfanities)); |
| break; |
| } |
| #endif |
| case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT: { |
| GURL url(chrome::kSpeechInputAboutURL); |
| GURL localized_url = google_util::AppendGoogleLocaleParam(url); |
| // Open URL with no referrer field (because user clicked on menu item). |
| OpenURL(localized_url, GURL(), 0, NEW_FOREGROUND_TAB, |
| content::PAGE_TRANSITION_LINK); |
| break; |
| } |
| |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| ProtocolHandlerRegistry::ProtocolHandlerList |
| RenderViewContextMenu::GetHandlersForLinkUrl() { |
| ProtocolHandlerRegistry::ProtocolHandlerList handlers = |
| protocol_handler_registry_->GetHandlersFor(params_.link_url.scheme()); |
| std::sort(handlers.begin(), handlers.end()); |
| return handlers; |
| } |
| |
| void RenderViewContextMenu::MenuWillShow(ui::SimpleMenuModel* source) { |
| for (int i = 0; i < source->GetItemCount(); ++i) { |
| if (source->IsVisibleAt(i) && |
| source->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR) { |
| RecordShownItem(source->GetCommandIdAt(i)); |
| } |
| } |
| |
| // Ignore notifications from submenus. |
| if (source != &menu_model_) |
| return; |
| |
| content::RenderWidgetHostView* view = |
| source_web_contents_->GetRenderWidgetHostView(); |
| if (view) |
| view->SetShowingContextMenu(true); |
| |
| content::NotificationService::current()->Notify( |
| chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN, |
| content::Source<RenderViewContextMenu>(this), |
| content::NotificationService::NoDetails()); |
| } |
| |
| void RenderViewContextMenu::MenuClosed(ui::SimpleMenuModel* source) { |
| // Ignore notifications from submenus. |
| if (source != &menu_model_) |
| return; |
| |
| content::RenderWidgetHostView* view = |
| source_web_contents_->GetRenderWidgetHostView(); |
| if (view) |
| view->SetShowingContextMenu(false); |
| RenderViewHost* rvh = source_web_contents_->GetRenderViewHost(); |
| if (rvh) { |
| rvh->NotifyContextMenuClosed(params_.custom_context); |
| } |
| |
| content::NotificationService::current()->Notify( |
| chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_CLOSED, |
| content::Source<RenderViewContextMenu>(this), |
| content::NotificationService::NoDetails()); |
| |
| if (!command_executed_) { |
| FOR_EACH_OBSERVER(RenderViewContextMenuObserver, |
| observers_, |
| OnMenuCancel()); |
| } |
| } |
| |
| bool RenderViewContextMenu::IsDevCommandEnabled(int id) const { |
| if (id == IDC_CONTENT_CONTEXT_INSPECTELEMENT || |
| id == IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE) { |
| const CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (!profile_->GetPrefs()->GetBoolean(prefs::kWebKitJavascriptEnabled) || |
| command_line->HasSwitch(switches::kDisableJavaScript)) |
| return false; |
| |
| // Don't enable the web inspector if the developer tools are disabled via |
| // the preference dev-tools-disabled. |
| if (profile_->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| string16 RenderViewContextMenu::PrintableSelectionText() { |
| return ui::TruncateString(params_.selection_text, |
| kMaxSelectionTextLength); |
| } |
| |
| // Controller functions -------------------------------------------------------- |
| |
| void RenderViewContextMenu::OpenURL( |
| const GURL& url, const GURL& referrer, int64 frame_id, |
| WindowOpenDisposition disposition, |
| content::PageTransition transition) { |
| // Ensure that URL fragment, username and password fields are not sent |
| // in the referrer. |
| GURL sanitized_referrer(referrer); |
| if (sanitized_referrer.is_valid() && (sanitized_referrer.has_ref() || |
| sanitized_referrer.has_username() || sanitized_referrer.has_password())) { |
| GURL::Replacements referrer_mods; |
| referrer_mods.ClearRef(); |
| referrer_mods.ClearUsername(); |
| referrer_mods.ClearPassword(); |
| sanitized_referrer = sanitized_referrer.ReplaceComponents(referrer_mods); |
| } |
| |
| WebContents* new_contents = source_web_contents_->OpenURL(OpenURLParams( |
| url, content::Referrer(sanitized_referrer, params_.referrer_policy), |
| disposition, transition, false)); |
| if (!new_contents) |
| return; |
| |
| RetargetingDetails details; |
| details.source_web_contents = source_web_contents_; |
| details.source_frame_id = frame_id; |
| details.target_url = url; |
| details.target_web_contents = new_contents; |
| details.not_yet_in_tabstrip = false; |
| content::NotificationService::current()->Notify( |
| chrome::NOTIFICATION_RETARGETING, |
| content::Source<Profile>(Profile::FromBrowserContext( |
| source_web_contents_->GetBrowserContext())), |
| content::Details<RetargetingDetails>(&details)); |
| } |
| |
| void RenderViewContextMenu::CopyImageAt(int x, int y) { |
| source_web_contents_->GetRenderViewHost()->CopyImageAt(x, y); |
| } |
| |
| void RenderViewContextMenu::Inspect(int x, int y) { |
| content::RecordAction(UserMetricsAction("DevTools_InspectElement")); |
| source_web_contents_->GetRenderViewHostAtPosition( |
| x, y, base::Bind(&DevToolsInspectElementAt)); |
| } |
| |
| void RenderViewContextMenu::WriteURLToClipboard(const GURL& url) { |
| chrome_common_net::WriteURLToClipboard( |
| url, |
| profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), |
| ui::Clipboard::GetForCurrentThread()); |
| } |
| |
| void RenderViewContextMenu::MediaPlayerActionAt( |
| const gfx::Point& location, |
| const WebMediaPlayerAction& action) { |
| source_web_contents_->GetRenderViewHost()-> |
| ExecuteMediaPlayerActionAtLocation(location, action); |
| } |
| |
| void RenderViewContextMenu::PluginActionAt( |
| const gfx::Point& location, |
| const WebPluginAction& action) { |
| source_web_contents_->GetRenderViewHost()-> |
| ExecutePluginActionAtLocation(location, action); |
| } |