GoogleURLTrackerInfoBarDelegate: Initialize uninitialized member in constructor.
[chromium-blink-merge.git] / chrome / browser / renderer_context_menu / render_view_context_menu.cc
blob88159fd724baa70dd5b701aca1a220024f483134
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
7 #include <algorithm>
8 #include <set>
9 #include <utility>
11 #include "apps/app_load_service.h"
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_member.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/time/time.h"
22 #include "chrome/app/chrome_command_ids.h"
23 #include "chrome/browser/app_mode/app_mode_utils.h"
24 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
25 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
26 #include "chrome/browser/autocomplete/autocomplete_input.h"
27 #include "chrome/browser/autocomplete/autocomplete_match.h"
28 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/chrome_notification_types.h"
30 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
31 #include "chrome/browser/devtools/devtools_window.h"
32 #include "chrome/browser/download/download_service.h"
33 #include "chrome/browser/download/download_service_factory.h"
34 #include "chrome/browser/download/download_stats.h"
35 #include "chrome/browser/extensions/devtools_util.h"
36 #include "chrome/browser/extensions/extension_service.h"
37 #include "chrome/browser/google/google_util.h"
38 #include "chrome/browser/guest_view/web_view/web_view_guest.h"
39 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
40 #include "chrome/browser/prefs/incognito_mode_prefs.h"
41 #include "chrome/browser/profiles/profile.h"
42 #include "chrome/browser/profiles/profile_io_data.h"
43 #include "chrome/browser/renderer_context_menu/context_menu_content_type_factory.h"
44 #include "chrome/browser/renderer_context_menu/spellchecker_submenu_observer.h"
45 #include "chrome/browser/renderer_context_menu/spelling_menu_observer.h"
46 #include "chrome/browser/search/search.h"
47 #include "chrome/browser/search_engines/search_terms_data.h"
48 #include "chrome/browser/search_engines/template_url.h"
49 #include "chrome/browser/search_engines/template_url_service.h"
50 #include "chrome/browser/search_engines/template_url_service_factory.h"
51 #include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
52 #include "chrome/browser/spellchecker/spellcheck_service.h"
53 #include "chrome/browser/tab_contents/retargeting_details.h"
54 #include "chrome/browser/translate/translate_service.h"
55 #include "chrome/browser/translate/translate_tab_helper.h"
56 #include "chrome/browser/ui/browser.h"
57 #include "chrome/browser/ui/browser_commands.h"
58 #include "chrome/browser/ui/browser_finder.h"
59 #include "chrome/browser/ui/chrome_pages.h"
60 #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h"
61 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
62 #include "chrome/common/chrome_constants.h"
63 #include "chrome/common/chrome_switches.h"
64 #include "chrome/common/content_restriction.h"
65 #include "chrome/common/net/url_util.h"
66 #include "chrome/common/pref_names.h"
67 #include "chrome/common/render_messages.h"
68 #include "chrome/common/spellcheck_messages.h"
69 #include "chrome/common/url_constants.h"
70 #include "components/translate/core/browser/translate_download_manager.h"
71 #include "components/translate/core/browser/translate_manager.h"
72 #include "components/translate/core/browser/translate_prefs.h"
73 #include "content/public/browser/child_process_security_policy.h"
74 #include "content/public/browser/download_manager.h"
75 #include "content/public/browser/download_save_info.h"
76 #include "content/public/browser/download_url_parameters.h"
77 #include "content/public/browser/navigation_details.h"
78 #include "content/public/browser/navigation_entry.h"
79 #include "content/public/browser/notification_service.h"
80 #include "content/public/browser/render_frame_host.h"
81 #include "content/public/browser/render_process_host.h"
82 #include "content/public/browser/render_view_host.h"
83 #include "content/public/browser/render_widget_host_view.h"
84 #include "content/public/browser/user_metrics.h"
85 #include "content/public/browser/web_contents.h"
86 #include "content/public/common/menu_item.h"
87 #include "content/public/common/ssl_status.h"
88 #include "content/public/common/url_utils.h"
89 #include "extensions/browser/extension_host.h"
90 #include "extensions/browser/extension_system.h"
91 #include "extensions/browser/view_type_utils.h"
92 #include "extensions/common/extension.h"
93 #include "grit/generated_resources.h"
94 #include "net/base/escape.h"
95 #include "third_party/WebKit/public/web/WebContextMenuData.h"
96 #include "third_party/WebKit/public/web/WebMediaPlayerAction.h"
97 #include "third_party/WebKit/public/web/WebPluginAction.h"
98 #include "ui/base/clipboard/clipboard.h"
99 #include "ui/base/l10n/l10n_util.h"
100 #include "ui/gfx/favicon_size.h"
101 #include "ui/gfx/point.h"
102 #include "ui/gfx/size.h"
103 #include "ui/gfx/text_elider.h"
105 #if defined(ENABLE_PRINTING)
106 #include "chrome/common/print_messages.h"
108 #if defined(ENABLE_FULL_PRINTING)
109 #include "chrome/browser/printing/print_preview_context_menu_observer.h"
110 #include "chrome/browser/printing/print_preview_dialog_controller.h"
111 #include "chrome/browser/printing/print_view_manager.h"
112 #else
113 #include "chrome/browser/printing/print_view_manager_basic.h"
114 #endif // defined(ENABLE_FULL_PRINTING)
115 #endif // defined(ENABLE_PRINTING)
117 using base::UserMetricsAction;
118 using blink::WebContextMenuData;
119 using blink::WebMediaPlayerAction;
120 using blink::WebPluginAction;
121 using blink::WebString;
122 using blink::WebURL;
123 using content::BrowserContext;
124 using content::ChildProcessSecurityPolicy;
125 using content::DownloadManager;
126 using content::DownloadUrlParameters;
127 using content::NavigationController;
128 using content::NavigationEntry;
129 using content::OpenURLParams;
130 using content::RenderFrameHost;
131 using content::RenderViewHost;
132 using content::SSLStatus;
133 using content::WebContents;
134 using extensions::Extension;
135 using extensions::MenuItem;
136 using extensions::MenuManager;
138 namespace {
140 const int kImageSearchThumbnailMinSize = 300 * 300;
141 const int kImageSearchThumbnailMaxWidth = 600;
142 const int kImageSearchThumbnailMaxHeight = 600;
144 // Maps UMA enumeration to IDC. IDC could be changed so we can't use
145 // just them and |UMA_HISTOGRAM_CUSTOM_ENUMERATION|.
146 // Never change mapping or reuse |enum_id|. Always push back new items.
147 // Items that is not used any more by |RenderViewContextMenu.ExecuteCommand|
148 // could be deleted, but don't change the rest of |kUmaEnumToControlId|.
149 const struct UmaEnumCommandIdPair {
150 int enum_id;
151 int control_id;
152 } kUmaEnumToControlId[] = {
153 { 0, IDC_CONTENT_CONTEXT_CUSTOM_FIRST },
154 { 1, IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST },
155 { 2, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST },
156 { 3, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB },
157 { 4, IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW },
158 { 5, IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD },
159 { 6, IDC_CONTENT_CONTEXT_SAVELINKAS },
160 { 7, IDC_CONTENT_CONTEXT_SAVEAVAS },
161 { 8, IDC_CONTENT_CONTEXT_SAVEIMAGEAS },
162 { 9, IDC_CONTENT_CONTEXT_COPYLINKLOCATION },
163 { 10, IDC_CONTENT_CONTEXT_COPYIMAGELOCATION },
164 { 11, IDC_CONTENT_CONTEXT_COPYAVLOCATION },
165 { 12, IDC_CONTENT_CONTEXT_COPYIMAGE },
166 { 13, IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB },
167 { 14, IDC_CONTENT_CONTEXT_OPENAVNEWTAB },
168 { 15, IDC_CONTENT_CONTEXT_PLAYPAUSE },
169 { 16, IDC_CONTENT_CONTEXT_MUTE },
170 { 17, IDC_CONTENT_CONTEXT_LOOP },
171 { 18, IDC_CONTENT_CONTEXT_CONTROLS },
172 { 19, IDC_CONTENT_CONTEXT_ROTATECW },
173 { 20, IDC_CONTENT_CONTEXT_ROTATECCW },
174 { 21, IDC_BACK },
175 { 22, IDC_FORWARD },
176 { 23, IDC_SAVE_PAGE },
177 { 24, IDC_RELOAD },
178 { 25, IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP },
179 { 26, IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP },
180 { 27, IDC_PRINT },
181 { 28, IDC_VIEW_SOURCE },
182 { 29, IDC_CONTENT_CONTEXT_INSPECTELEMENT },
183 { 30, IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE },
184 { 31, IDC_CONTENT_CONTEXT_VIEWPAGEINFO },
185 { 32, IDC_CONTENT_CONTEXT_TRANSLATE },
186 { 33, IDC_CONTENT_CONTEXT_RELOADFRAME },
187 { 34, IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE },
188 { 35, IDC_CONTENT_CONTEXT_VIEWFRAMEINFO },
189 { 36, IDC_CONTENT_CONTEXT_UNDO },
190 { 37, IDC_CONTENT_CONTEXT_REDO },
191 { 38, IDC_CONTENT_CONTEXT_CUT },
192 { 39, IDC_CONTENT_CONTEXT_COPY },
193 { 40, IDC_CONTENT_CONTEXT_PASTE },
194 { 41, IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE },
195 { 42, IDC_CONTENT_CONTEXT_DELETE },
196 { 43, IDC_CONTENT_CONTEXT_SELECTALL },
197 { 44, IDC_CONTENT_CONTEXT_SEARCHWEBFOR },
198 { 45, IDC_CONTENT_CONTEXT_GOTOURL },
199 { 46, IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS },
200 { 47, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS },
201 { 48, IDC_CONTENT_CONTEXT_ADDSEARCHENGINE },
202 { 52, IDC_CONTENT_CONTEXT_OPENLINKWITH },
203 { 53, IDC_CHECK_SPELLING_WHILE_TYPING },
204 { 54, IDC_SPELLCHECK_MENU },
205 { 55, IDC_CONTENT_CONTEXT_SPELLING_TOGGLE },
206 { 56, IDC_SPELLCHECK_LANGUAGES_FIRST },
207 { 57, IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE },
208 { 58, IDC_SPELLCHECK_SUGGESTION_0 },
209 // Add new items here and use |enum_id| from the next line.
210 { 59, 0 }, // Must be the last. Increment |enum_id| when new IDC was added.
213 // Collapses large ranges of ids before looking for UMA enum.
214 int CollapleCommandsForUMA(int id) {
215 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
216 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
217 return IDC_CONTENT_CONTEXT_CUSTOM_FIRST;
220 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
221 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
222 return IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST;
225 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
226 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
227 return IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
230 if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
231 id <= IDC_SPELLCHECK_LANGUAGES_LAST) {
232 return IDC_SPELLCHECK_LANGUAGES_FIRST;
235 if (id >= IDC_SPELLCHECK_SUGGESTION_0 &&
236 id <= IDC_SPELLCHECK_SUGGESTION_LAST) {
237 return IDC_SPELLCHECK_SUGGESTION_0;
240 return id;
243 // Returns UMA enum value for command specified by |id| or -1 if not found.
244 int FindUMAEnumValueForCommand(int id) {
245 id = CollapleCommandsForUMA(id);
246 const size_t kMappingSize = arraysize(kUmaEnumToControlId);
247 for (size_t i = 0; i < kMappingSize; ++i) {
248 if (kUmaEnumToControlId[i].control_id == id) {
249 return kUmaEnumToControlId[i].enum_id;
252 return -1;
255 // Increments histogram value for used items specified by |id|.
256 void RecordUsedItem(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.Used", enum_id,
261 kUmaEnumToControlId[kMappingSize - 1].enum_id);
262 } else {
263 NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id;
267 // Increments histogram value for visible context menu item specified by |id|.
268 void RecordShownItem(int id) {
269 int enum_id = FindUMAEnumValueForCommand(id);
270 if (enum_id != -1) {
271 const size_t kMappingSize = arraysize(kUmaEnumToControlId);
272 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Shown", enum_id,
273 kUmaEnumToControlId[kMappingSize - 1].enum_id);
274 } else {
275 // Just warning here. It's harder to maintain list of all possibly
276 // visible items than executable items.
277 DLOG(ERROR) << "Update kUmaEnumToControlId. Unhanded IDC: " << id;
281 // Usually a new tab is expected where this function is used,
282 // however users should be able to open a tab in background
283 // or in a new window.
284 WindowOpenDisposition ForceNewTabDispositionFromEventFlags(
285 int event_flags) {
286 WindowOpenDisposition disposition =
287 ui::DispositionFromEventFlags(event_flags);
288 return disposition == CURRENT_TAB ? NEW_FOREGROUND_TAB : disposition;
291 bool IsCustomItemEnabled(const std::vector<content::MenuItem>& items, int id) {
292 DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
293 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST);
294 for (size_t i = 0; i < items.size(); ++i) {
295 int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action;
296 if (action_id == id)
297 return items[i].enabled;
298 if (items[i].type == content::MenuItem::SUBMENU) {
299 if (IsCustomItemEnabled(items[i].submenu, id))
300 return true;
303 return false;
306 bool IsCustomItemChecked(const std::vector<content::MenuItem>& items, int id) {
307 DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
308 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST);
309 for (size_t i = 0; i < items.size(); ++i) {
310 int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action;
311 if (action_id == id)
312 return items[i].checked;
313 if (items[i].type == content::MenuItem::SUBMENU) {
314 if (IsCustomItemChecked(items[i].submenu, id))
315 return true;
318 return false;
321 const size_t kMaxCustomMenuDepth = 5;
322 const size_t kMaxCustomMenuTotalItems = 1000;
324 void AddCustomItemsToMenu(const std::vector<content::MenuItem>& items,
325 size_t depth,
326 size_t* total_items,
327 ui::SimpleMenuModel::Delegate* delegate,
328 ui::SimpleMenuModel* menu_model) {
329 if (depth > kMaxCustomMenuDepth) {
330 LOG(ERROR) << "Custom menu too deeply nested.";
331 return;
333 for (size_t i = 0; i < items.size(); ++i) {
334 if (IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action >=
335 IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
336 LOG(ERROR) << "Custom menu action value too big.";
337 return;
339 if (*total_items >= kMaxCustomMenuTotalItems) {
340 LOG(ERROR) << "Custom menu too large (too many items).";
341 return;
343 (*total_items)++;
344 switch (items[i].type) {
345 case content::MenuItem::OPTION:
346 menu_model->AddItem(
347 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
348 items[i].label);
349 break;
350 case content::MenuItem::CHECKABLE_OPTION:
351 menu_model->AddCheckItem(
352 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
353 items[i].label);
354 break;
355 case content::MenuItem::GROUP:
356 // TODO(viettrungluu): I don't know what this is supposed to do.
357 NOTREACHED();
358 break;
359 case content::MenuItem::SEPARATOR:
360 menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
361 break;
362 case content::MenuItem::SUBMENU: {
363 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate);
364 AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate,
365 submenu);
366 menu_model->AddSubMenu(
367 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
368 items[i].label,
369 submenu);
370 break;
372 default:
373 NOTREACHED();
374 break;
379 // Helper function to escape "&" as "&&".
380 void EscapeAmpersands(base::string16* text) {
381 base::ReplaceChars(*text, base::ASCIIToUTF16("&"), base::ASCIIToUTF16("&&"),
382 text);
385 } // namespace
387 // static
388 const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50;
390 // static
391 bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) {
392 return url.SchemeIs(content::kChromeDevToolsScheme);
395 // static
396 bool RenderViewContextMenu::IsInternalResourcesURL(const GURL& url) {
397 if (!url.SchemeIs(content::kChromeUIScheme))
398 return false;
399 return url.host() == chrome::kChromeUISyncResourcesHost;
402 static const int kSpellcheckRadioGroup = 1;
404 RenderViewContextMenu::RenderViewContextMenu(
405 content::RenderFrameHost* render_frame_host,
406 const content::ContextMenuParams& params)
407 : params_(params),
408 source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)),
409 render_process_id_(render_frame_host->GetProcess()->GetID()),
410 render_frame_id_(render_frame_host->GetRoutingID()),
411 profile_(Profile::FromBrowserContext(
412 source_web_contents_->GetBrowserContext())),
413 menu_model_(this),
414 extension_items_(profile_,
415 this,
416 &menu_model_,
417 base::Bind(MenuItemMatchesParams, params_)),
418 protocol_handler_submenu_model_(this),
419 protocol_handler_registry_(
420 ProtocolHandlerRegistryFactory::GetForProfile(profile_)),
421 command_executed_(false) {
422 content_type_.reset(ContextMenuContentTypeFactory::Create(
423 source_web_contents_, params));
426 RenderViewContextMenu::~RenderViewContextMenu() {
429 // Menu construction functions -------------------------------------------------
431 void RenderViewContextMenu::Init() {
432 InitMenu();
433 PlatformInit();
436 void RenderViewContextMenu::Cancel() {
437 PlatformCancel();
440 static bool ExtensionPatternMatch(const extensions::URLPatternSet& patterns,
441 const GURL& url) {
442 // No patterns means no restriction, so that implicitly matches.
443 if (patterns.is_empty())
444 return true;
445 return patterns.MatchesURL(url);
448 // static
449 bool RenderViewContextMenu::ExtensionContextAndPatternMatch(
450 const content::ContextMenuParams& params,
451 MenuItem::ContextList contexts,
452 const extensions::URLPatternSet& target_url_patterns) {
453 const bool has_link = !params.link_url.is_empty();
454 const bool has_selection = !params.selection_text.empty();
455 const bool in_frame = !params.frame_url.is_empty();
457 if (contexts.Contains(MenuItem::ALL) ||
458 (has_selection && contexts.Contains(MenuItem::SELECTION)) ||
459 (params.is_editable && contexts.Contains(MenuItem::EDITABLE)) ||
460 (in_frame && contexts.Contains(MenuItem::FRAME)))
461 return true;
463 if (has_link && contexts.Contains(MenuItem::LINK) &&
464 ExtensionPatternMatch(target_url_patterns, params.link_url))
465 return true;
467 switch (params.media_type) {
468 case WebContextMenuData::MediaTypeImage:
469 if (contexts.Contains(MenuItem::IMAGE) &&
470 ExtensionPatternMatch(target_url_patterns, params.src_url))
471 return true;
472 break;
474 case WebContextMenuData::MediaTypeVideo:
475 if (contexts.Contains(MenuItem::VIDEO) &&
476 ExtensionPatternMatch(target_url_patterns, params.src_url))
477 return true;
478 break;
480 case WebContextMenuData::MediaTypeAudio:
481 if (contexts.Contains(MenuItem::AUDIO) &&
482 ExtensionPatternMatch(target_url_patterns, params.src_url))
483 return true;
484 break;
486 default:
487 break;
490 // PAGE is the least specific context, so we only examine that if none of the
491 // other contexts apply (except for FRAME, which is included in PAGE for
492 // backwards compatibility).
493 if (!has_link && !has_selection && !params.is_editable &&
494 params.media_type == WebContextMenuData::MediaTypeNone &&
495 contexts.Contains(MenuItem::PAGE))
496 return true;
498 return false;
501 static const GURL& GetDocumentURL(const content::ContextMenuParams& params) {
502 return params.frame_url.is_empty() ? params.page_url : params.frame_url;
505 // static
506 bool RenderViewContextMenu::MenuItemMatchesParams(
507 const content::ContextMenuParams& params,
508 const extensions::MenuItem* item) {
509 bool match = ExtensionContextAndPatternMatch(params, item->contexts(),
510 item->target_url_patterns());
511 if (!match)
512 return false;
514 const GURL& document_url = GetDocumentURL(params);
515 return ExtensionPatternMatch(item->document_url_patterns(), document_url);
518 void RenderViewContextMenu::AppendAllExtensionItems() {
519 extension_items_.Clear();
520 ExtensionService* service =
521 extensions::ExtensionSystem::Get(profile_)->extension_service();
522 if (!service)
523 return; // In unit-tests, we may not have an ExtensionService.
525 MenuManager* menu_manager = MenuManager::Get(profile_);
526 if (!menu_manager)
527 return;
529 base::string16 printable_selection_text = PrintableSelectionText();
530 EscapeAmpersands(&printable_selection_text);
532 // Get a list of extension id's that have context menu items, and sort by the
533 // top level context menu title of the extension.
534 std::set<MenuItem::ExtensionKey> ids = menu_manager->ExtensionIds();
535 std::vector<base::string16> sorted_menu_titles;
536 std::map<base::string16, std::string> map_ids;
537 for (std::set<MenuItem::ExtensionKey>::iterator i = ids.begin();
538 i != ids.end();
539 ++i) {
540 const Extension* extension =
541 service->GetExtensionById(i->extension_id, false);
542 // Platform apps have their context menus created directly in
543 // AppendPlatformAppItems.
544 if (extension && !extension->is_platform_app()) {
545 base::string16 menu_title = extension_items_.GetTopLevelContextMenuTitle(
546 *i, printable_selection_text);
547 map_ids[menu_title] = i->extension_id;
548 sorted_menu_titles.push_back(menu_title);
551 if (sorted_menu_titles.empty())
552 return;
554 const std::string app_locale = g_browser_process->GetApplicationLocale();
555 l10n_util::SortStrings16(app_locale, &sorted_menu_titles);
557 int index = 0;
558 base::TimeTicks begin = base::TimeTicks::Now();
559 for (size_t i = 0; i < sorted_menu_titles.size(); ++i) {
560 const std::string& id = map_ids[sorted_menu_titles[i]];
561 const MenuItem::ExtensionKey extension_key(id);
562 extension_items_.AppendExtensionItems(
563 extension_key, printable_selection_text, &index);
566 UMA_HISTOGRAM_TIMES("Extensions.ContextMenus_BuildTime",
567 base::TimeTicks::Now() - begin);
568 UMA_HISTOGRAM_COUNTS("Extensions.ContextMenus_ItemCount", index);
571 void RenderViewContextMenu::AppendCurrentExtensionItems() {
572 // Avoid appending extension related items when |extension| is null.
573 // For Panel, this happens when the panel is navigated to a url outside of the
574 // extension's package.
575 const Extension* extension = GetExtension();
576 if (extension) {
577 // Only add extension items from this extension.
578 int index = 0;
579 const MenuItem::ExtensionKey key(
580 extension->id(), WebViewGuest::GetViewInstanceId(source_web_contents_));
581 extension_items_.AppendExtensionItems(
582 key, PrintableSelectionText(), &index);
586 void RenderViewContextMenu::InitMenu() {
587 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_CUSTOM)) {
588 AppendCustomItems();
590 const bool has_selection = !params_.selection_text.empty();
591 if (has_selection) {
592 // We will add more items if there's a selection, so add a separator.
593 // TODO(lazyboy): Clean up separator logic.
594 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
598 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PAGE))
599 AppendPageItems();
601 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_FRAME)) {
602 // Merge in frame items with page items if we clicked within a frame that
603 // needs them.
604 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
605 AppendFrameItems();
608 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_LINK)) {
609 AppendLinkItems();
610 if (params_.media_type != WebContextMenuData::MediaTypeNone)
611 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
614 if (content_type_->SupportsGroup(
615 ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE)) {
616 AppendImageItems();
619 if (content_type_->SupportsGroup(
620 ContextMenuContentType::ITEM_GROUP_SEARCHWEBFORIMAGE)) {
621 AppendSearchWebForImageItems();
624 if (content_type_->SupportsGroup(
625 ContextMenuContentType::ITEM_GROUP_MEDIA_VIDEO)) {
626 AppendVideoItems();
629 if (content_type_->SupportsGroup(
630 ContextMenuContentType::ITEM_GROUP_MEDIA_AUDIO)) {
631 AppendAudioItems();
634 if (content_type_->SupportsGroup(
635 ContextMenuContentType::ITEM_GROUP_MEDIA_CANVAS)) {
636 AppendCanvasItems();
639 if (content_type_->SupportsGroup(
640 ContextMenuContentType::ITEM_GROUP_MEDIA_PLUGIN)) {
641 AppendPluginItems();
644 // ITEM_GROUP_MEDIA_FILE has no specific items.
646 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_EDITABLE))
647 AppendEditableItems();
649 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_COPY)) {
650 DCHECK(!content_type_->SupportsGroup(
651 ContextMenuContentType::ITEM_GROUP_EDITABLE));
652 AppendCopyItem();
655 if (content_type_->SupportsGroup(
656 ContextMenuContentType::ITEM_GROUP_SEARCH_PROVIDER)) {
657 AppendSearchProvider();
660 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PRINT))
661 AppendPrintItem();
663 if (content_type_->SupportsGroup(
664 ContextMenuContentType::ITEM_GROUP_ALL_EXTENSION)) {
665 DCHECK(!content_type_->SupportsGroup(
666 ContextMenuContentType::ITEM_GROUP_CURRENT_EXTENSION));
667 AppendAllExtensionItems();
670 if (content_type_->SupportsGroup(
671 ContextMenuContentType::ITEM_GROUP_CURRENT_EXTENSION)) {
672 DCHECK(!content_type_->SupportsGroup(
673 ContextMenuContentType::ITEM_GROUP_ALL_EXTENSION));
674 AppendCurrentExtensionItems();
677 if (content_type_->SupportsGroup(
678 ContextMenuContentType::ITEM_GROUP_DEVELOPER)) {
679 AppendDeveloperItems();
682 if (content_type_->SupportsGroup(
683 ContextMenuContentType::ITEM_GROUP_DEVTOOLS_UNPACKED_EXT)) {
684 AppendDevtoolsForUnpackedExtensions();
687 if (content_type_->SupportsGroup(
688 ContextMenuContentType::ITEM_GROUP_PRINT_PREVIEW)) {
689 AppendPrintPreviewItems();
693 void RenderViewContextMenu::AppendPrintPreviewItems() {
694 #if defined(ENABLE_FULL_PRINTING)
695 if (!print_preview_menu_observer_.get()) {
696 print_preview_menu_observer_.reset(
697 new PrintPreviewContextMenuObserver(source_web_contents_));
700 observers_.AddObserver(print_preview_menu_observer_.get());
701 #endif
704 const Extension* RenderViewContextMenu::GetExtension() const {
705 extensions::ExtensionSystem* system =
706 extensions::ExtensionSystem::Get(profile_);
707 // There is no process manager in some tests.
708 if (!system->process_manager())
709 return NULL;
711 return system->process_manager()->GetExtensionForRenderViewHost(
712 source_web_contents_->GetRenderViewHost());
715 void RenderViewContextMenu::AddMenuItem(int command_id,
716 const base::string16& title) {
717 menu_model_.AddItem(command_id, title);
720 void RenderViewContextMenu::AddCheckItem(int command_id,
721 const base::string16& title) {
722 menu_model_.AddCheckItem(command_id, title);
725 void RenderViewContextMenu::AddSeparator() {
726 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
729 void RenderViewContextMenu::AddSubMenu(int command_id,
730 const base::string16& label,
731 ui::MenuModel* model) {
732 menu_model_.AddSubMenu(command_id, label, model);
735 void RenderViewContextMenu::UpdateMenuItem(int command_id,
736 bool enabled,
737 bool hidden,
738 const base::string16& label) {
739 // This function needs platform-specific implementation.
740 NOTIMPLEMENTED();
743 RenderViewHost* RenderViewContextMenu::GetRenderViewHost() const {
744 return source_web_contents_->GetRenderViewHost();
747 WebContents* RenderViewContextMenu::GetWebContents() const {
748 return source_web_contents_;
751 Profile* RenderViewContextMenu::GetProfile() const {
752 return profile_;
755 bool RenderViewContextMenu::AppendCustomItems() {
756 size_t total_items = 0;
757 AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this,
758 &menu_model_);
759 return total_items > 0;
762 void RenderViewContextMenu::AppendDeveloperItems() {
763 // Show Inspect Element in DevTools itself only in case of the debug
764 // devtools build.
765 bool show_developer_items = !IsDevToolsURL(params_.page_url);
767 #if defined(DEBUG_DEVTOOLS)
768 show_developer_items = true;
769 #endif
771 if (!show_developer_items)
772 return;
774 // In the DevTools popup menu, "developer items" is normally the only
775 // section, so omit the separator there.
776 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
777 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTELEMENT,
778 IDS_CONTENT_CONTEXT_INSPECTELEMENT);
781 void RenderViewContextMenu::AppendDevtoolsForUnpackedExtensions() {
782 // Add a separator if there are any items already in the menu.
783 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
785 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP,
786 IDS_CONTENT_CONTEXT_RELOAD_PACKAGED_APP);
787 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP,
788 IDS_CONTENT_CONTEXT_RESTART_APP);
789 AppendDeveloperItems();
790 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE,
791 IDS_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE);
794 void RenderViewContextMenu::AppendLinkItems() {
795 if (!params_.link_url.is_empty()) {
796 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB,
797 IDS_CONTENT_CONTEXT_OPENLINKNEWTAB);
798 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW,
799 IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW);
800 if (params_.link_url.is_valid()) {
801 AppendProtocolHandlerSubMenu();
804 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD,
805 IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD);
806 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVELINKAS,
807 IDS_CONTENT_CONTEXT_SAVELINKAS);
810 menu_model_.AddItemWithStringId(
811 IDC_CONTENT_CONTEXT_COPYLINKLOCATION,
812 params_.link_url.SchemeIs(url::kMailToScheme) ?
813 IDS_CONTENT_CONTEXT_COPYEMAILADDRESS :
814 IDS_CONTENT_CONTEXT_COPYLINKLOCATION);
817 void RenderViewContextMenu::AppendImageItems() {
818 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS,
819 IDS_CONTENT_CONTEXT_SAVEIMAGEAS);
820 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION,
821 IDS_CONTENT_CONTEXT_COPYIMAGELOCATION);
822 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGE,
823 IDS_CONTENT_CONTEXT_COPYIMAGE);
824 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB,
825 IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB);
828 void RenderViewContextMenu::AppendSearchWebForImageItems() {
829 const TemplateURL* const default_provider =
830 TemplateURLServiceFactory::GetForProfile(profile_)->
831 GetDefaultSearchProvider();
832 if (params_.has_image_contents && default_provider &&
833 !default_provider->image_url().empty() &&
834 default_provider->image_url_ref().IsValid()) {
835 menu_model_.AddItem(
836 IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
837 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
838 default_provider->short_name()));
842 void RenderViewContextMenu::AppendAudioItems() {
843 AppendMediaItems();
844 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
845 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
846 IDS_CONTENT_CONTEXT_SAVEAUDIOAS);
847 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION,
848 IDS_CONTENT_CONTEXT_COPYAUDIOLOCATION);
849 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB,
850 IDS_CONTENT_CONTEXT_OPENAUDIONEWTAB);
853 void RenderViewContextMenu::AppendCanvasItems() {
854 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS,
855 IDS_CONTENT_CONTEXT_SAVEIMAGEAS);
857 // TODO(zino): We should support 'copy image' for canvas.
858 // http://crbug.com/369092
861 void RenderViewContextMenu::AppendVideoItems() {
862 AppendMediaItems();
863 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
864 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
865 IDS_CONTENT_CONTEXT_SAVEVIDEOAS);
866 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION,
867 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION);
868 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB,
869 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB);
872 void RenderViewContextMenu::AppendMediaItems() {
873 int media_flags = params_.media_flags;
875 menu_model_.AddItemWithStringId(
876 IDC_CONTENT_CONTEXT_PLAYPAUSE,
877 media_flags & WebContextMenuData::MediaPaused ?
878 IDS_CONTENT_CONTEXT_PLAY :
879 IDS_CONTENT_CONTEXT_PAUSE);
881 menu_model_.AddItemWithStringId(
882 IDC_CONTENT_CONTEXT_MUTE,
883 media_flags & WebContextMenuData::MediaMuted ?
884 IDS_CONTENT_CONTEXT_UNMUTE :
885 IDS_CONTENT_CONTEXT_MUTE);
887 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_LOOP,
888 IDS_CONTENT_CONTEXT_LOOP);
889 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_CONTROLS,
890 IDS_CONTENT_CONTEXT_CONTROLS);
893 void RenderViewContextMenu::AppendPluginItems() {
894 if (params_.page_url == params_.src_url) {
895 // Full page plugin, so show page menu items.
896 if (params_.link_url.is_empty() && params_.selection_text.empty())
897 AppendPageItems();
898 } else {
899 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
900 IDS_CONTENT_CONTEXT_SAVEPAGEAS);
901 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
904 if (params_.media_flags & WebContextMenuData::MediaCanRotate) {
905 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
906 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECW,
907 IDS_CONTENT_CONTEXT_ROTATECW);
908 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECCW,
909 IDS_CONTENT_CONTEXT_ROTATECCW);
913 void RenderViewContextMenu::AppendPageItems() {
914 menu_model_.AddItemWithStringId(IDC_BACK, IDS_CONTENT_CONTEXT_BACK);
915 menu_model_.AddItemWithStringId(IDC_FORWARD, IDS_CONTENT_CONTEXT_FORWARD);
916 menu_model_.AddItemWithStringId(IDC_RELOAD, IDS_CONTENT_CONTEXT_RELOAD);
917 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
918 menu_model_.AddItemWithStringId(IDC_SAVE_PAGE,
919 IDS_CONTENT_CONTEXT_SAVEPAGEAS);
920 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
922 if (TranslateService::IsTranslatableURL(params_.page_url)) {
923 std::string locale = g_browser_process->GetApplicationLocale();
924 locale = TranslateDownloadManager::GetLanguageCode(locale);
925 base::string16 language =
926 l10n_util::GetDisplayNameForLocale(locale, locale, true);
927 menu_model_.AddItem(
928 IDC_CONTENT_CONTEXT_TRANSLATE,
929 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE, language));
932 menu_model_.AddItemWithStringId(IDC_VIEW_SOURCE,
933 IDS_CONTENT_CONTEXT_VIEWPAGESOURCE);
934 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWPAGEINFO,
935 IDS_CONTENT_CONTEXT_VIEWPAGEINFO);
938 void RenderViewContextMenu::AppendFrameItems() {
939 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOADFRAME,
940 IDS_CONTENT_CONTEXT_RELOADFRAME);
941 // These two menu items have yet to be implemented.
942 // http://code.google.com/p/chromium/issues/detail?id=11827
943 // IDS_CONTENT_CONTEXT_SAVEFRAMEAS
944 // IDS_CONTENT_CONTEXT_PRINTFRAME
945 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE,
946 IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE);
947 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMEINFO,
948 IDS_CONTENT_CONTEXT_VIEWFRAMEINFO);
951 void RenderViewContextMenu::AppendCopyItem() {
952 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY,
953 IDS_CONTENT_CONTEXT_COPY);
956 void RenderViewContextMenu::AppendPrintItem() {
957 if (profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) &&
958 (params_.media_type == WebContextMenuData::MediaTypeNone ||
959 params_.media_flags & WebContextMenuData::MediaCanPrint)) {
960 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
964 void RenderViewContextMenu::AppendSearchProvider() {
965 DCHECK(profile_);
967 base::TrimWhitespace(params_.selection_text, base::TRIM_ALL,
968 &params_.selection_text);
969 if (params_.selection_text.empty())
970 return;
972 base::ReplaceChars(params_.selection_text, AutocompleteMatch::kInvalidChars,
973 base::ASCIIToUTF16(" "), &params_.selection_text);
975 AutocompleteMatch match;
976 AutocompleteClassifierFactory::GetForProfile(profile_)->Classify(
977 params_.selection_text, false, false, AutocompleteInput::INVALID_SPEC,
978 &match, NULL);
979 selection_navigation_url_ = match.destination_url;
980 if (!selection_navigation_url_.is_valid())
981 return;
983 base::string16 printable_selection_text = PrintableSelectionText();
984 EscapeAmpersands(&printable_selection_text);
986 if (AutocompleteMatch::IsSearchType(match.type)) {
987 const TemplateURL* const default_provider =
988 TemplateURLServiceFactory::GetForProfile(profile_)->
989 GetDefaultSearchProvider();
990 if (!default_provider)
991 return;
992 menu_model_.AddItem(
993 IDC_CONTENT_CONTEXT_SEARCHWEBFOR,
994 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR,
995 default_provider->short_name(),
996 printable_selection_text));
997 } else {
998 if ((selection_navigation_url_ != params_.link_url) &&
999 ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1000 selection_navigation_url_.scheme())) {
1001 menu_model_.AddItem(
1002 IDC_CONTENT_CONTEXT_GOTOURL,
1003 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_GOTOURL,
1004 printable_selection_text));
1009 void RenderViewContextMenu::AppendEditableItems() {
1010 const bool use_spellcheck_and_search = !chrome::IsRunningInForcedAppMode();
1012 if (use_spellcheck_and_search)
1013 AppendSpellingSuggestionsSubMenu();
1015 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO,
1016 IDS_CONTENT_CONTEXT_UNDO);
1017 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO,
1018 IDS_CONTENT_CONTEXT_REDO);
1019 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1020 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_CUT,
1021 IDS_CONTENT_CONTEXT_CUT);
1022 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY,
1023 IDS_CONTENT_CONTEXT_COPY);
1024 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE,
1025 IDS_CONTENT_CONTEXT_PASTE);
1026 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE,
1027 IDS_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE);
1028 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_DELETE,
1029 IDS_CONTENT_CONTEXT_DELETE);
1030 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1032 if (use_spellcheck_and_search && !params_.keyword_url.is_empty()) {
1033 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ADDSEARCHENGINE,
1034 IDS_CONTENT_CONTEXT_ADDSEARCHENGINE);
1035 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1038 if (use_spellcheck_and_search)
1039 AppendSpellcheckOptionsSubMenu();
1040 AppendPlatformEditableItems();
1042 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1043 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SELECTALL,
1044 IDS_CONTENT_CONTEXT_SELECTALL);
1047 void RenderViewContextMenu::AppendSpellingSuggestionsSubMenu() {
1048 if (!spelling_menu_observer_.get())
1049 spelling_menu_observer_.reset(new SpellingMenuObserver(this));
1050 observers_.AddObserver(spelling_menu_observer_.get());
1051 spelling_menu_observer_->InitMenu(params_);
1054 void RenderViewContextMenu::AppendSpellcheckOptionsSubMenu() {
1055 if (!spellchecker_submenu_observer_.get()) {
1056 spellchecker_submenu_observer_.reset(new SpellCheckerSubMenuObserver(
1057 this, this, kSpellcheckRadioGroup));
1059 spellchecker_submenu_observer_->InitMenu(params_);
1060 observers_.AddObserver(spellchecker_submenu_observer_.get());
1063 void RenderViewContextMenu::AppendProtocolHandlerSubMenu() {
1064 const ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1065 GetHandlersForLinkUrl();
1066 if (handlers.empty())
1067 return;
1068 size_t max = IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST -
1069 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
1070 for (size_t i = 0; i < handlers.size() && i <= max; i++) {
1071 protocol_handler_submenu_model_.AddItem(
1072 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST + i,
1073 base::UTF8ToUTF16(handlers[i].url().host()));
1075 protocol_handler_submenu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
1076 protocol_handler_submenu_model_.AddItem(
1077 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS,
1078 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH_CONFIGURE));
1080 menu_model_.AddSubMenu(
1081 IDC_CONTENT_CONTEXT_OPENLINKWITH,
1082 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH),
1083 &protocol_handler_submenu_model_);
1086 void RenderViewContextMenu::AppendPlatformEditableItems() {
1089 // Menu delegate functions -----------------------------------------------------
1091 bool RenderViewContextMenu::IsCommandIdEnabled(int id) const {
1092 // If this command is is added by one of our observers, we dispatch it to the
1093 // observer.
1094 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
1095 RenderViewContextMenuObserver* observer;
1096 while ((observer = it.GetNext()) != NULL) {
1097 if (observer->IsCommandIdSupported(id))
1098 return observer->IsCommandIdEnabled(id);
1101 CoreTabHelper* core_tab_helper =
1102 CoreTabHelper::FromWebContents(source_web_contents_);
1103 int content_restrictions = 0;
1104 if (core_tab_helper)
1105 content_restrictions = core_tab_helper->content_restrictions();
1106 if (id == IDC_PRINT && (content_restrictions & CONTENT_RESTRICTION_PRINT))
1107 return false;
1109 if (id == IDC_SAVE_PAGE &&
1110 (content_restrictions & CONTENT_RESTRICTION_SAVE)) {
1111 return false;
1114 // Allow Spell Check language items on sub menu for text area context menu.
1115 if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) &&
1116 (id < IDC_SPELLCHECK_LANGUAGES_LAST)) {
1117 return profile_->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck);
1120 // Custom items.
1121 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1122 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1123 return IsCustomItemEnabled(params_.custom_items, id);
1126 // Extension items.
1127 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
1128 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
1129 return extension_items_.IsCommandIdEnabled(id);
1132 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
1133 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
1134 return true;
1137 IncognitoModePrefs::Availability incognito_avail =
1138 IncognitoModePrefs::GetAvailability(profile_->GetPrefs());
1139 switch (id) {
1140 case IDC_BACK:
1141 return source_web_contents_->GetController().CanGoBack();
1143 case IDC_FORWARD:
1144 return source_web_contents_->GetController().CanGoForward();
1146 case IDC_RELOAD: {
1147 CoreTabHelper* core_tab_helper =
1148 CoreTabHelper::FromWebContents(source_web_contents_);
1149 if (!core_tab_helper)
1150 return false;
1152 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
1153 return !core_delegate ||
1154 core_delegate->CanReloadContents(source_web_contents_);
1157 case IDC_VIEW_SOURCE:
1158 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
1159 return source_web_contents_->GetController().CanViewSource();
1161 case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
1162 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE:
1163 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP:
1164 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP:
1165 return IsDevCommandEnabled(id);
1167 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO:
1168 if (source_web_contents_->GetController().GetVisibleEntry() == NULL)
1169 return false;
1170 // Disabled if no browser is associated (e.g. desktop notifications).
1171 if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL)
1172 return false;
1173 return true;
1175 case IDC_CONTENT_CONTEXT_TRANSLATE: {
1176 TranslateTabHelper* translate_tab_helper =
1177 TranslateTabHelper::FromWebContents(source_web_contents_);
1178 if (!translate_tab_helper)
1179 return false;
1180 std::string original_lang =
1181 translate_tab_helper->GetLanguageState().original_language();
1182 std::string target_lang = g_browser_process->GetApplicationLocale();
1183 target_lang = TranslateDownloadManager::GetLanguageCode(target_lang);
1184 // Note that we intentionally enable the menu even if the original and
1185 // target languages are identical. This is to give a way to user to
1186 // translate a page that might contains text fragments in a different
1187 // language.
1188 return ((params_.edit_flags & WebContextMenuData::CanTranslate) != 0) &&
1189 !original_lang.empty() && // Did we receive the page language yet?
1190 !translate_tab_helper->GetLanguageState().IsPageTranslated() &&
1191 !source_web_contents_->GetInterstitialPage() &&
1192 // There are some application locales which can't be used as a
1193 // target language for translation.
1194 TranslateDownloadManager::IsSupportedLanguage(target_lang) &&
1195 // Disable on the Instant Extended NTP.
1196 !chrome::IsInstantNTP(source_web_contents_);
1199 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB:
1200 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
1201 return params_.link_url.is_valid();
1203 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION:
1204 return params_.unfiltered_link_url.is_valid();
1206 case IDC_CONTENT_CONTEXT_SAVELINKAS: {
1207 PrefService* local_state = g_browser_process->local_state();
1208 DCHECK(local_state);
1209 // Test if file-selection dialogs are forbidden by policy.
1210 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1211 return false;
1213 return params_.link_url.is_valid() &&
1214 ProfileIOData::IsHandledProtocol(params_.link_url.scheme());
1217 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: {
1218 PrefService* local_state = g_browser_process->local_state();
1219 DCHECK(local_state);
1220 // Test if file-selection dialogs are forbidden by policy.
1221 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1222 return false;
1224 if (params_.media_type == WebContextMenuData::MediaTypeCanvas)
1225 return true;
1227 return params_.src_url.is_valid() &&
1228 ProfileIOData::IsHandledProtocol(params_.src_url.scheme());
1231 // The images shown in the most visited thumbnails can't be opened or
1232 // searched for conventionally.
1233 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
1234 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
1235 return params_.src_url.is_valid() &&
1236 (params_.src_url.scheme() != content::kChromeUIScheme);
1238 case IDC_CONTENT_CONTEXT_COPYIMAGE:
1239 return params_.has_image_contents;
1241 // Media control commands should all be disabled if the player is in an
1242 // error state.
1243 case IDC_CONTENT_CONTEXT_PLAYPAUSE:
1244 case IDC_CONTENT_CONTEXT_LOOP:
1245 return (params_.media_flags &
1246 WebContextMenuData::MediaInError) == 0;
1248 // Mute and unmute should also be disabled if the player has no audio.
1249 case IDC_CONTENT_CONTEXT_MUTE:
1250 return (params_.media_flags &
1251 WebContextMenuData::MediaHasAudio) != 0 &&
1252 (params_.media_flags &
1253 WebContextMenuData::MediaInError) == 0;
1255 case IDC_CONTENT_CONTEXT_CONTROLS:
1256 return (params_.media_flags &
1257 WebContextMenuData::MediaCanToggleControls) != 0;
1259 case IDC_CONTENT_CONTEXT_ROTATECW:
1260 case IDC_CONTENT_CONTEXT_ROTATECCW:
1261 return
1262 (params_.media_flags & WebContextMenuData::MediaCanRotate) != 0;
1264 case IDC_CONTENT_CONTEXT_COPYAVLOCATION:
1265 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION:
1266 return params_.src_url.is_valid();
1268 case IDC_CONTENT_CONTEXT_SAVEAVAS: {
1269 PrefService* local_state = g_browser_process->local_state();
1270 DCHECK(local_state);
1271 // Test if file-selection dialogs are forbidden by policy.
1272 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1273 return false;
1275 const GURL& url = params_.src_url;
1276 bool can_save =
1277 (params_.media_flags & WebContextMenuData::MediaCanSave) &&
1278 url.is_valid() && ProfileIOData::IsHandledProtocol(url.scheme());
1279 #if defined(ENABLE_FULL_PRINTING)
1280 // Do not save the preview PDF on the print preview page.
1281 can_save = can_save &&
1282 !(printing::PrintPreviewDialogController::IsPrintPreviewURL(url));
1283 #endif
1284 return can_save;
1287 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
1288 return true;
1290 case IDC_SAVE_PAGE: {
1291 CoreTabHelper* core_tab_helper =
1292 CoreTabHelper::FromWebContents(source_web_contents_);
1293 if (!core_tab_helper)
1294 return false;
1296 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
1297 if (core_delegate &&
1298 !core_delegate->CanSaveContents(source_web_contents_))
1299 return false;
1301 PrefService* local_state = g_browser_process->local_state();
1302 DCHECK(local_state);
1303 // Test if file-selection dialogs are forbidden by policy.
1304 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs))
1305 return false;
1307 // We save the last committed entry (which the user is looking at), as
1308 // opposed to any pending URL that hasn't committed yet.
1309 NavigationEntry* entry =
1310 source_web_contents_->GetController().GetLastCommittedEntry();
1311 return content::IsSavableURL(entry ? entry->GetURL() : GURL());
1314 case IDC_CONTENT_CONTEXT_RELOADFRAME:
1315 return params_.frame_url.is_valid();
1317 case IDC_CONTENT_CONTEXT_UNDO:
1318 return !!(params_.edit_flags & WebContextMenuData::CanUndo);
1320 case IDC_CONTENT_CONTEXT_REDO:
1321 return !!(params_.edit_flags & WebContextMenuData::CanRedo);
1323 case IDC_CONTENT_CONTEXT_CUT:
1324 return !!(params_.edit_flags & WebContextMenuData::CanCut);
1326 case IDC_CONTENT_CONTEXT_COPY:
1327 return !!(params_.edit_flags & WebContextMenuData::CanCopy);
1329 case IDC_CONTENT_CONTEXT_PASTE:
1330 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE:
1331 return !!(params_.edit_flags & WebContextMenuData::CanPaste);
1333 case IDC_CONTENT_CONTEXT_DELETE:
1334 return !!(params_.edit_flags & WebContextMenuData::CanDelete);
1336 case IDC_CONTENT_CONTEXT_SELECTALL:
1337 return !!(params_.edit_flags & WebContextMenuData::CanSelectAll);
1339 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
1340 return !profile_->IsOffTheRecord() && params_.link_url.is_valid() &&
1341 incognito_avail != IncognitoModePrefs::DISABLED;
1343 case IDC_PRINT:
1344 return profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) &&
1345 (params_.media_type == WebContextMenuData::MediaTypeNone ||
1346 params_.media_flags & WebContextMenuData::MediaCanPrint);
1348 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR:
1349 case IDC_CONTENT_CONTEXT_GOTOURL:
1350 case IDC_SPELLPANEL_TOGGLE:
1351 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS:
1352 return true;
1353 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO:
1354 // Disabled if no browser is associated (e.g. desktop notifications).
1355 if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL)
1356 return false;
1357 return true;
1359 case IDC_CHECK_SPELLING_WHILE_TYPING:
1360 return profile_->GetPrefs()->GetBoolean(
1361 prefs::kEnableContinuousSpellcheck);
1363 #if !defined(OS_MACOSX) && defined(OS_POSIX)
1364 // TODO(suzhe): this should not be enabled for password fields.
1365 case IDC_INPUT_METHODS_MENU:
1366 return true;
1367 #endif
1369 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE:
1370 return !params_.keyword_url.is_empty();
1372 case IDC_SPELLCHECK_MENU:
1373 return true;
1375 case IDC_CONTENT_CONTEXT_OPENLINKWITH:
1376 return true;
1378 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS:
1379 return true;
1381 default:
1382 NOTREACHED();
1383 return false;
1387 bool RenderViewContextMenu::IsCommandIdChecked(int id) const {
1388 // If this command is is added by one of our observers, we dispatch it to the
1389 // observer.
1390 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
1391 RenderViewContextMenuObserver* observer;
1392 while ((observer = it.GetNext()) != NULL) {
1393 if (observer->IsCommandIdSupported(id))
1394 return observer->IsCommandIdChecked(id);
1397 // See if the video is set to looping.
1398 if (id == IDC_CONTENT_CONTEXT_LOOP) {
1399 return (params_.media_flags &
1400 WebContextMenuData::MediaLoop) != 0;
1403 if (id == IDC_CONTENT_CONTEXT_CONTROLS) {
1404 return (params_.media_flags &
1405 WebContextMenuData::MediaControls) != 0;
1408 // Custom items.
1409 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1410 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1411 return IsCustomItemChecked(params_.custom_items, id);
1414 // Extension items.
1415 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
1416 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
1417 return extension_items_.IsCommandIdChecked(id);
1420 return false;
1423 void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) {
1424 command_executed_ = true;
1425 RecordUsedItem(id);
1427 // If this command is is added by one of our observers, we dispatch it to the
1428 // observer.
1429 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
1430 RenderViewContextMenuObserver* observer;
1431 while ((observer = it.GetNext()) != NULL) {
1432 if (observer->IsCommandIdSupported(id))
1433 return observer->ExecuteCommand(id);
1436 RenderFrameHost* render_frame_host =
1437 RenderFrameHost::FromID(render_process_id_, render_frame_id_);
1439 // Process custom actions range.
1440 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST &&
1441 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) {
1442 unsigned action = id - IDC_CONTENT_CONTEXT_CUSTOM_FIRST;
1443 const content::CustomContextMenuContext& context = params_.custom_context;
1444 #if defined(ENABLE_PLUGINS)
1445 if (context.request_id && !context.is_pepper_menu) {
1446 ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins(
1447 source_web_contents_, false, std::string());
1449 #endif
1450 source_web_contents_->ExecuteCustomContextMenuCommand(action, context);
1451 return;
1454 // Process extension menu items.
1455 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
1456 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
1457 extension_items_.ExecuteCommand(id, source_web_contents_, params_);
1458 return;
1461 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST &&
1462 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) {
1463 ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1464 GetHandlersForLinkUrl();
1465 if (handlers.empty()) {
1466 return;
1468 content::RecordAction(
1469 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Open"));
1470 int handlerIndex = id - IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST;
1471 WindowOpenDisposition disposition =
1472 ForceNewTabDispositionFromEventFlags(event_flags);
1473 OpenURL(
1474 handlers[handlerIndex].TranslateUrl(params_.link_url),
1475 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1476 disposition, content::PAGE_TRANSITION_LINK);
1477 return;
1480 switch (id) {
1481 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: {
1482 Browser* browser =
1483 chrome::FindBrowserWithWebContents(source_web_contents_);
1484 OpenURL(
1485 params_.link_url,
1486 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1487 !browser || browser->is_app() ?
1488 NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB,
1489 content::PAGE_TRANSITION_LINK);
1490 break;
1492 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
1493 OpenURL(
1494 params_.link_url,
1495 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1496 NEW_WINDOW, content::PAGE_TRANSITION_LINK);
1497 break;
1499 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
1500 OpenURL(params_.link_url, GURL(), OFF_THE_RECORD,
1501 content::PAGE_TRANSITION_LINK);
1502 break;
1504 case IDC_CONTENT_CONTEXT_SAVELINKAS: {
1505 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
1506 const GURL& referrer =
1507 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url;
1508 const GURL& url = params_.link_url;
1509 DownloadManager* dlm = BrowserContext::GetDownloadManager(profile_);
1510 scoped_ptr<DownloadUrlParameters> dl_params(
1511 DownloadUrlParameters::FromWebContents(source_web_contents_, url));
1512 dl_params->set_referrer(
1513 content::Referrer(referrer, params_.referrer_policy));
1514 dl_params->set_referrer_encoding(params_.frame_charset);
1515 dl_params->set_prompt(true);
1516 dlm->DownloadUrl(dl_params.Pass());
1517 break;
1520 case IDC_CONTENT_CONTEXT_SAVEAVAS:
1521 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: {
1522 if (params_.media_type == WebContextMenuData::MediaTypeCanvas) {
1523 source_web_contents_->GetRenderViewHost()->SaveImageAt(
1524 params_.x, params_.y);
1525 } else {
1526 // TODO(zino): We can use SaveImageAt() like a case of canvas.
1527 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
1528 const GURL& referrer =
1529 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url;
1530 const GURL& url = params_.src_url;
1531 source_web_contents_->SaveFrame(url, content::Referrer(
1532 referrer, params_.referrer_policy));
1534 break;
1537 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION:
1538 WriteURLToClipboard(params_.unfiltered_link_url);
1539 break;
1541 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION:
1542 case IDC_CONTENT_CONTEXT_COPYAVLOCATION:
1543 WriteURLToClipboard(params_.src_url);
1544 break;
1546 case IDC_CONTENT_CONTEXT_COPYIMAGE:
1547 CopyImageAt(params_.x, params_.y);
1548 break;
1550 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
1551 GetImageThumbnailForSearch();
1552 break;
1554 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
1555 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
1556 OpenURL(
1557 params_.src_url,
1558 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url,
1559 NEW_BACKGROUND_TAB, content::PAGE_TRANSITION_LINK);
1560 break;
1562 case IDC_CONTENT_CONTEXT_PLAYPAUSE: {
1563 bool play = !!(params_.media_flags & WebContextMenuData::MediaPaused);
1564 if (play) {
1565 content::RecordAction(UserMetricsAction("MediaContextMenu_Play"));
1566 } else {
1567 content::RecordAction(UserMetricsAction("MediaContextMenu_Pause"));
1569 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1570 WebMediaPlayerAction(
1571 WebMediaPlayerAction::Play, play));
1572 break;
1575 case IDC_CONTENT_CONTEXT_MUTE: {
1576 bool mute = !(params_.media_flags & WebContextMenuData::MediaMuted);
1577 if (mute) {
1578 content::RecordAction(UserMetricsAction("MediaContextMenu_Mute"));
1579 } else {
1580 content::RecordAction(UserMetricsAction("MediaContextMenu_Unmute"));
1582 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1583 WebMediaPlayerAction(
1584 WebMediaPlayerAction::Mute, mute));
1585 break;
1588 case IDC_CONTENT_CONTEXT_LOOP:
1589 content::RecordAction(UserMetricsAction("MediaContextMenu_Loop"));
1590 MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
1591 WebMediaPlayerAction(
1592 WebMediaPlayerAction::Loop,
1593 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP)));
1594 break;
1596 case IDC_CONTENT_CONTEXT_CONTROLS:
1597 content::RecordAction(UserMetricsAction("MediaContextMenu_Controls"));
1598 MediaPlayerActionAt(
1599 gfx::Point(params_.x, params_.y),
1600 WebMediaPlayerAction(
1601 WebMediaPlayerAction::Controls,
1602 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS)));
1603 break;
1605 case IDC_CONTENT_CONTEXT_ROTATECW:
1606 content::RecordAction(
1607 UserMetricsAction("PluginContextMenu_RotateClockwise"));
1608 PluginActionAt(
1609 gfx::Point(params_.x, params_.y),
1610 WebPluginAction(
1611 WebPluginAction::Rotate90Clockwise,
1612 true));
1613 break;
1615 case IDC_CONTENT_CONTEXT_ROTATECCW:
1616 content::RecordAction(
1617 UserMetricsAction("PluginContextMenu_RotateCounterclockwise"));
1618 PluginActionAt(
1619 gfx::Point(params_.x, params_.y),
1620 WebPluginAction(
1621 WebPluginAction::Rotate90Counterclockwise,
1622 true));
1623 break;
1625 case IDC_BACK:
1626 source_web_contents_->GetController().GoBack();
1627 break;
1629 case IDC_FORWARD:
1630 source_web_contents_->GetController().GoForward();
1631 break;
1633 case IDC_SAVE_PAGE:
1634 source_web_contents_->OnSavePage();
1635 break;
1637 case IDC_RELOAD:
1638 source_web_contents_->GetController().Reload(true);
1639 break;
1641 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP: {
1642 const Extension* platform_app = GetExtension();
1643 DCHECK(platform_app);
1644 DCHECK(platform_app->is_platform_app());
1646 extensions::ExtensionSystem::Get(profile_)->extension_service()->
1647 ReloadExtension(platform_app->id());
1648 break;
1651 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP: {
1652 const Extension* platform_app = GetExtension();
1653 DCHECK(platform_app);
1654 DCHECK(platform_app->is_platform_app());
1656 apps::AppLoadService::Get(profile_)->RestartApplication(
1657 platform_app->id());
1658 break;
1661 case IDC_PRINT:
1662 #if defined(ENABLE_PRINTING)
1663 if (params_.media_type == WebContextMenuData::MediaTypeNone) {
1664 #if defined(ENABLE_FULL_PRINTING)
1665 printing::PrintViewManager* print_view_manager =
1666 printing::PrintViewManager::FromWebContents(source_web_contents_);
1668 if (!print_view_manager)
1669 break;
1670 if (profile_->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled)) {
1671 print_view_manager->PrintNow();
1672 } else {
1673 print_view_manager->PrintPreviewNow(!params_.selection_text.empty());
1675 #else
1676 printing::PrintViewManagerBasic* print_view_manager =
1677 printing::PrintViewManagerBasic::FromWebContents(
1678 source_web_contents_);
1679 if (!print_view_manager)
1680 break;
1681 print_view_manager->PrintNow();
1682 #endif // defined(ENABLE_FULL_PRINTING)
1683 } else {
1684 if (render_frame_host) {
1685 render_frame_host->Send(new PrintMsg_PrintNodeUnderContextMenu(
1686 render_frame_host->GetRoutingID()));
1689 #endif // defined(ENABLE_PRINTING)
1690 break;
1692 case IDC_VIEW_SOURCE:
1693 source_web_contents_->ViewSource();
1694 break;
1696 case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
1697 Inspect(params_.x, params_.y);
1698 break;
1700 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE: {
1701 const Extension* platform_app = GetExtension();
1702 DCHECK(platform_app);
1703 DCHECK(platform_app->is_platform_app());
1705 extensions::devtools_util::InspectBackgroundPage(platform_app, profile_);
1706 break;
1709 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: {
1710 NavigationController* controller = &source_web_contents_->GetController();
1711 // Important to use GetVisibleEntry to match what's showing in the
1712 // omnibox. This may return null.
1713 NavigationEntry* nav_entry = controller->GetVisibleEntry();
1714 if (!nav_entry)
1715 return;
1716 Browser* browser =
1717 chrome::FindBrowserWithWebContents(source_web_contents_);
1718 chrome::ShowWebsiteSettings(browser, source_web_contents_,
1719 nav_entry->GetURL(), nav_entry->GetSSL());
1720 break;
1723 case IDC_CONTENT_CONTEXT_TRANSLATE: {
1724 // A translation might have been triggered by the time the menu got
1725 // selected, do nothing in that case.
1726 TranslateTabHelper* translate_tab_helper =
1727 TranslateTabHelper::FromWebContents(source_web_contents_);
1728 if (!translate_tab_helper ||
1729 translate_tab_helper->GetLanguageState().IsPageTranslated() ||
1730 translate_tab_helper->GetLanguageState().translation_pending()) {
1731 return;
1733 std::string original_lang =
1734 translate_tab_helper->GetLanguageState().original_language();
1735 std::string target_lang = g_browser_process->GetApplicationLocale();
1736 target_lang = TranslateDownloadManager::GetLanguageCode(target_lang);
1737 // Since the user decided to translate for that language and site, clears
1738 // any preferences for not translating them.
1739 scoped_ptr<TranslatePrefs> prefs(
1740 TranslateTabHelper::CreateTranslatePrefs(profile_->GetPrefs()));
1741 prefs->UnblockLanguage(original_lang);
1742 prefs->RemoveSiteFromBlacklist(params_.page_url.HostNoBrackets());
1743 TranslateManager* manager = translate_tab_helper->GetTranslateManager();
1744 DCHECK(manager);
1745 manager->TranslatePage(original_lang, target_lang, true);
1746 break;
1749 case IDC_CONTENT_CONTEXT_RELOADFRAME:
1750 // We always obey the cache here.
1751 // TODO(evanm): Perhaps we could allow shift-clicking the menu item to do
1752 // a cache-ignoring reload of the frame.
1753 source_web_contents_->ReloadFocusedFrame(false);
1754 break;
1756 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
1757 source_web_contents_->ViewFrameSource(params_.frame_url,
1758 params_.frame_page_state);
1759 break;
1761 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO: {
1762 Browser* browser = chrome::FindBrowserWithWebContents(
1763 source_web_contents_);
1764 chrome::ShowWebsiteSettings(browser, source_web_contents_,
1765 params_.frame_url, params_.security_info);
1766 break;
1769 case IDC_CONTENT_CONTEXT_UNDO:
1770 source_web_contents_->Undo();
1771 break;
1773 case IDC_CONTENT_CONTEXT_REDO:
1774 source_web_contents_->Redo();
1775 break;
1777 case IDC_CONTENT_CONTEXT_CUT:
1778 source_web_contents_->Cut();
1779 break;
1781 case IDC_CONTENT_CONTEXT_COPY:
1782 source_web_contents_->Copy();
1783 break;
1785 case IDC_CONTENT_CONTEXT_PASTE:
1786 source_web_contents_->Paste();
1787 break;
1789 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE:
1790 source_web_contents_->PasteAndMatchStyle();
1791 break;
1793 case IDC_CONTENT_CONTEXT_DELETE:
1794 source_web_contents_->Delete();
1795 break;
1797 case IDC_CONTENT_CONTEXT_SELECTALL:
1798 source_web_contents_->SelectAll();
1799 break;
1801 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR:
1802 case IDC_CONTENT_CONTEXT_GOTOURL: {
1803 WindowOpenDisposition disposition =
1804 ForceNewTabDispositionFromEventFlags(event_flags);
1805 OpenURL(selection_navigation_url_, GURL(), disposition,
1806 content::PAGE_TRANSITION_LINK);
1807 break;
1809 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS: {
1810 WindowOpenDisposition disposition =
1811 ForceNewTabDispositionFromEventFlags(event_flags);
1812 GURL url = chrome::GetSettingsUrl(chrome::kLanguageOptionsSubPage);
1813 OpenURL(url, GURL(), disposition, content::PAGE_TRANSITION_LINK);
1814 break;
1817 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS: {
1818 content::RecordAction(
1819 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Settings"));
1820 WindowOpenDisposition disposition =
1821 ForceNewTabDispositionFromEventFlags(event_flags);
1822 GURL url = chrome::GetSettingsUrl(chrome::kHandlerSettingsSubPage);
1823 OpenURL(url, GURL(), disposition, content::PAGE_TRANSITION_LINK);
1824 break;
1827 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE: {
1828 // Make sure the model is loaded.
1829 TemplateURLService* model =
1830 TemplateURLServiceFactory::GetForProfile(profile_);
1831 if (!model)
1832 return;
1833 model->Load();
1835 SearchEngineTabHelper* search_engine_tab_helper =
1836 SearchEngineTabHelper::FromWebContents(source_web_contents_);
1837 if (search_engine_tab_helper &&
1838 search_engine_tab_helper->delegate()) {
1839 base::string16 keyword(
1840 TemplateURLService::GenerateKeyword(params_.page_url));
1841 TemplateURLData data;
1842 data.short_name = keyword;
1843 data.SetKeyword(keyword);
1844 data.SetURL(params_.keyword_url.spec());
1845 data.favicon_url =
1846 TemplateURL::GenerateFaviconURL(params_.page_url.GetOrigin());
1847 // Takes ownership of the TemplateURL.
1848 search_engine_tab_helper->delegate()->
1849 ConfirmAddSearchProvider(new TemplateURL(profile_, data), profile_);
1851 break;
1854 default:
1855 NOTREACHED();
1856 break;
1860 ProtocolHandlerRegistry::ProtocolHandlerList
1861 RenderViewContextMenu::GetHandlersForLinkUrl() {
1862 ProtocolHandlerRegistry::ProtocolHandlerList handlers =
1863 protocol_handler_registry_->GetHandlersFor(params_.link_url.scheme());
1864 std::sort(handlers.begin(), handlers.end());
1865 return handlers;
1868 void RenderViewContextMenu::MenuWillShow(ui::SimpleMenuModel* source) {
1869 for (int i = 0; i < source->GetItemCount(); ++i) {
1870 if (source->IsVisibleAt(i) &&
1871 source->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR) {
1872 RecordShownItem(source->GetCommandIdAt(i));
1876 // Ignore notifications from submenus.
1877 if (source != &menu_model_)
1878 return;
1880 content::RenderWidgetHostView* view =
1881 source_web_contents_->GetRenderWidgetHostView();
1882 if (view)
1883 view->SetShowingContextMenu(true);
1885 content::NotificationService::current()->Notify(
1886 chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN,
1887 content::Source<RenderViewContextMenu>(this),
1888 content::NotificationService::NoDetails());
1891 void RenderViewContextMenu::MenuClosed(ui::SimpleMenuModel* source) {
1892 // Ignore notifications from submenus.
1893 if (source != &menu_model_)
1894 return;
1896 content::RenderWidgetHostView* view =
1897 source_web_contents_->GetRenderWidgetHostView();
1898 if (view)
1899 view->SetShowingContextMenu(false);
1900 source_web_contents_->NotifyContextMenuClosed(params_.custom_context);
1902 if (!command_executed_) {
1903 FOR_EACH_OBSERVER(RenderViewContextMenuObserver,
1904 observers_,
1905 OnMenuCancel());
1909 bool RenderViewContextMenu::IsDevCommandEnabled(int id) const {
1910 if (id == IDC_CONTENT_CONTEXT_INSPECTELEMENT ||
1911 id == IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE) {
1912 const CommandLine* command_line = CommandLine::ForCurrentProcess();
1913 if (!profile_->GetPrefs()->GetBoolean(prefs::kWebKitJavascriptEnabled) ||
1914 command_line->HasSwitch(switches::kDisableJavaScript))
1915 return false;
1917 // Don't enable the web inspector if the developer tools are disabled via
1918 // the preference dev-tools-disabled.
1919 if (profile_->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled))
1920 return false;
1923 return true;
1926 base::string16 RenderViewContextMenu::PrintableSelectionText() {
1927 return gfx::TruncateString(params_.selection_text,
1928 kMaxSelectionTextLength);
1931 // Controller functions --------------------------------------------------------
1933 void RenderViewContextMenu::OpenURL(
1934 const GURL& url, const GURL& referring_url,
1935 WindowOpenDisposition disposition,
1936 content::PageTransition transition) {
1937 content::Referrer referrer(referring_url.GetAsReferrer(),
1938 params_.referrer_policy);
1940 WebContents* new_contents = source_web_contents_->OpenURL(OpenURLParams(
1941 url, referrer, disposition, transition, false));
1942 if (!new_contents)
1943 return;
1945 RetargetingDetails details;
1946 details.source_web_contents = source_web_contents_;
1947 details.source_render_frame_id = render_frame_id_;
1948 details.target_url = url;
1949 details.target_web_contents = new_contents;
1950 details.not_yet_in_tabstrip = false;
1951 content::NotificationService::current()->Notify(
1952 chrome::NOTIFICATION_RETARGETING,
1953 content::Source<Profile>(Profile::FromBrowserContext(
1954 source_web_contents_->GetBrowserContext())),
1955 content::Details<RetargetingDetails>(&details));
1958 void RenderViewContextMenu::CopyImageAt(int x, int y) {
1959 source_web_contents_->GetRenderViewHost()->CopyImageAt(x, y);
1962 void RenderViewContextMenu::GetImageThumbnailForSearch() {
1963 RenderFrameHost* render_frame_host =
1964 RenderFrameHost::FromID(render_process_id_, render_frame_id_);
1965 if (!render_frame_host)
1966 return;
1967 render_frame_host->Send(new ChromeViewMsg_RequestThumbnailForContextNode(
1968 render_frame_host->GetRoutingID(),
1969 kImageSearchThumbnailMinSize,
1970 gfx::Size(kImageSearchThumbnailMaxWidth,
1971 kImageSearchThumbnailMaxHeight)));
1974 void RenderViewContextMenu::Inspect(int x, int y) {
1975 content::RecordAction(UserMetricsAction("DevTools_InspectElement"));
1976 RenderFrameHost* render_frame_host =
1977 RenderFrameHost::FromID(render_process_id_, render_frame_id_);
1978 if (!render_frame_host)
1979 return;
1980 DevToolsWindow::InspectElement(render_frame_host->GetRenderViewHost(), x, y);
1983 void RenderViewContextMenu::WriteURLToClipboard(const GURL& url) {
1984 chrome_common_net::WriteURLToClipboard(
1985 url,
1986 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages),
1987 ui::Clipboard::GetForCurrentThread());
1990 void RenderViewContextMenu::MediaPlayerActionAt(
1991 const gfx::Point& location,
1992 const WebMediaPlayerAction& action) {
1993 source_web_contents_->GetRenderViewHost()->
1994 ExecuteMediaPlayerActionAtLocation(location, action);
1997 void RenderViewContextMenu::PluginActionAt(
1998 const gfx::Point& location,
1999 const WebPluginAction& action) {
2000 source_web_contents_->GetRenderViewHost()->
2001 ExecutePluginActionAtLocation(location, action);