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