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