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