1 // Copyright (c) 2011 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 <algorithm> 6 #include <set> 7 8 #include "chrome/browser/tab_contents/render_view_context_menu.h" 9 10 #include "base/command_line.h" 11 #include "base/logging.h" 12 #include "base/metrics/histogram.h" 13 #include "base/stl_util-inl.h" 14 #include "base/string_util.h" 15 #include "base/time.h" 16 #include "base/utf_string_conversions.h" 17 #include "chrome/app/chrome_command_ids.h" 18 #include "chrome/browser/autocomplete/autocomplete_classifier.h" 19 #include "chrome/browser/autocomplete/autocomplete_edit.h" 20 #include "chrome/browser/autocomplete/autocomplete_match.h" 21 #include "chrome/browser/browser_process.h" 22 #include "chrome/browser/debugger/devtools_manager.h" 23 #include "chrome/browser/debugger/devtools_window.h" 24 #include "chrome/browser/download/download_manager.h" 25 #include "chrome/browser/download/download_util.h" 26 #include "chrome/browser/download/save_package.h" 27 #include "chrome/browser/extensions/extension_event_router.h" 28 #include "chrome/browser/extensions/extension_service.h" 29 #include "chrome/browser/metrics/user_metrics.h" 30 #include "chrome/browser/net/browser_url_util.h" 31 #include "chrome/browser/page_info_window.h" 32 #include "chrome/browser/platform_util.h" 33 #include "chrome/browser/prefs/pref_member.h" 34 #include "chrome/browser/prefs/pref_service.h" 35 #include "chrome/browser/printing/print_preview_tab_controller.h" 36 #include "chrome/browser/profiles/profile.h" 37 #include "chrome/browser/search_engines/template_url.h" 38 #include "chrome/browser/search_engines/template_url_model.h" 39 #include "chrome/browser/spellcheck_host.h" 40 #include "chrome/browser/spellchecker_platform_engine.h" 41 #include "chrome/browser/translate/translate_manager.h" 42 #include "chrome/browser/translate/translate_prefs.h" 43 #include "chrome/browser/translate/translate_tab_helper.h" 44 #include "chrome/browser/ui/download/download_tab_helper.h" 45 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 46 #include "chrome/common/chrome_constants.h" 47 #include "chrome/common/chrome_switches.h" 48 #include "chrome/common/content_restriction.h" 49 #include "chrome/common/pref_names.h" 50 #include "chrome/common/print_messages.h" 51 #include "chrome/common/url_constants.h" 52 #include "content/browser/child_process_security_policy.h" 53 #include "content/browser/renderer_host/render_view_host.h" 54 #include "content/browser/renderer_host/render_widget_host_view.h" 55 #include "content/browser/tab_contents/navigation_entry.h" 56 #include "content/browser/tab_contents/tab_contents.h" 57 #include "grit/generated_resources.h" 58 #include "net/base/escape.h" 59 #include "net/url_request/url_request.h" 60 #include "third_party/WebKit/Source/WebKit/chromium/public/WebContextMenuData.h" 61 #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerAction.h" 62 #include "third_party/WebKit/Source/WebKit/chromium/public/WebTextDirection.h" 63 #include "ui/base/l10n/l10n_util.h" 64 #include "ui/gfx/favicon_size.h" 65 #include "webkit/glue/webmenuitem.h" 66 67 using WebKit::WebContextMenuData; 68 using WebKit::WebMediaPlayerAction; 69 70 namespace { 71 72 bool IsCustomItemEnabled(const std::vector<WebMenuItem>& items, int id) { 73 DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && 74 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST); 75 for (size_t i = 0; i < items.size(); ++i) { 76 int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action; 77 if (action_id == id) 78 return items[i].enabled; 79 if (items[i].type == WebMenuItem::SUBMENU) { 80 if (IsCustomItemEnabled(items[i].submenu, id)) 81 return true; 82 } 83 } 84 return false; 85 } 86 87 bool IsCustomItemChecked(const std::vector<WebMenuItem>& items, int id) { 88 DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && 89 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST); 90 for (size_t i = 0; i < items.size(); ++i) { 91 int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action; 92 if (action_id == id) 93 return items[i].checked; 94 if (items[i].type == WebMenuItem::SUBMENU) { 95 if (IsCustomItemChecked(items[i].submenu, id)) 96 return true; 97 } 98 } 99 return false; 100 } 101 102 const size_t kMaxCustomMenuDepth = 5; 103 const size_t kMaxCustomMenuTotalItems = 1000; 104 105 void AddCustomItemsToMenu(const std::vector<WebMenuItem>& items, 106 size_t depth, 107 size_t* total_items, 108 ui::SimpleMenuModel::Delegate* delegate, 109 ui::SimpleMenuModel* menu_model) { 110 if (depth > kMaxCustomMenuDepth) { 111 LOG(ERROR) << "Custom menu too deeply nested."; 112 return; 113 } 114 for (size_t i = 0; i < items.size(); ++i) { 115 if (IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action >= 116 IDC_CONTENT_CONTEXT_CUSTOM_LAST) { 117 LOG(ERROR) << "Custom menu action value too big."; 118 return; 119 } 120 if (*total_items >= kMaxCustomMenuTotalItems) { 121 LOG(ERROR) << "Custom menu too large (too many items)."; 122 return; 123 } 124 (*total_items)++; 125 switch (items[i].type) { 126 case WebMenuItem::OPTION: 127 menu_model->AddItem( 128 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST, 129 items[i].label); 130 break; 131 case WebMenuItem::CHECKABLE_OPTION: 132 menu_model->AddCheckItem( 133 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST, 134 items[i].label); 135 break; 136 case WebMenuItem::GROUP: 137 // TODO(viettrungluu): I don't know what this is supposed to do. 138 NOTREACHED(); 139 break; 140 case WebMenuItem::SEPARATOR: 141 menu_model->AddSeparator(); 142 break; 143 case WebMenuItem::SUBMENU: { 144 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate); 145 AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate, 146 submenu); 147 menu_model->AddSubMenu( 148 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST, 149 items[i].label, 150 submenu); 151 break; 152 } 153 default: 154 NOTREACHED(); 155 break; 156 } 157 } 158 } 159 160 } // namespace 161 162 // static 163 const size_t RenderViewContextMenu::kMaxExtensionItemTitleLength = 75; 164 // static 165 const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50; 166 167 // static 168 bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) { 169 return url.SchemeIs(chrome::kChromeDevToolsScheme) && 170 url.host() == chrome::kChromeUIDevToolsHost; 171 } 172 173 // static 174 bool RenderViewContextMenu::IsInternalResourcesURL(const GURL& url) { 175 if (!url.SchemeIs(chrome::kChromeUIScheme)) 176 return false; 177 return url.host() == chrome::kChromeUISyncResourcesHost || 178 url.host() == chrome::kChromeUIRemotingResourcesHost; 179 } 180 181 static const int kSpellcheckRadioGroup = 1; 182 183 RenderViewContextMenu::RenderViewContextMenu( 184 TabContents* tab_contents, 185 const ContextMenuParams& params) 186 : params_(params), 187 source_tab_contents_(tab_contents), 188 profile_(tab_contents->profile()), 189 ALLOW_THIS_IN_INITIALIZER_LIST(menu_model_(this)), 190 external_(false), 191 ALLOW_THIS_IN_INITIALIZER_LIST(spellcheck_submenu_model_(this)), 192 ALLOW_THIS_IN_INITIALIZER_LIST(bidi_submenu_model_(this)) { 193 } 194 195 RenderViewContextMenu::~RenderViewContextMenu() { 196 } 197 198 // Menu construction functions ------------------------------------------------- 199 200 void RenderViewContextMenu::Init() { 201 InitMenu(); 202 PlatformInit(); 203 } 204 205 static bool ExtensionContextMatch(const ContextMenuParams& params, 206 ExtensionMenuItem::ContextList contexts) { 207 bool has_link = !params.link_url.is_empty(); 208 bool has_selection = !params.selection_text.empty(); 209 bool in_frame = !params.frame_url.is_empty(); 210 211 if (contexts.Contains(ExtensionMenuItem::ALL) || 212 (has_selection && contexts.Contains(ExtensionMenuItem::SELECTION)) || 213 (has_link && contexts.Contains(ExtensionMenuItem::LINK)) || 214 (params.is_editable && contexts.Contains(ExtensionMenuItem::EDITABLE)) || 215 (in_frame && contexts.Contains(ExtensionMenuItem::FRAME))) { 216 return true; 217 } 218 219 switch (params.media_type) { 220 case WebContextMenuData::MediaTypeImage: 221 return contexts.Contains(ExtensionMenuItem::IMAGE); 222 223 case WebContextMenuData::MediaTypeVideo: 224 return contexts.Contains(ExtensionMenuItem::VIDEO); 225 226 case WebContextMenuData::MediaTypeAudio: 227 return contexts.Contains(ExtensionMenuItem::AUDIO); 228 229 default: 230 break; 231 } 232 233 // PAGE is the least specific context, so we only examine that if none of the 234 // other contexts apply (except for FRAME, which is included in PAGE for 235 // backwards compatibility). 236 if (!has_link && !has_selection && !params.is_editable && 237 params.media_type == WebContextMenuData::MediaTypeNone && 238 contexts.Contains(ExtensionMenuItem::PAGE)) 239 return true; 240 241 return false; 242 } 243 244 static bool ExtensionPatternMatch(const ExtensionExtent& patterns, 245 const GURL& url) { 246 // No patterns means no restriction, so that implicitly matches. 247 if (patterns.is_empty()) 248 return true; 249 return patterns.ContainsURL(url); 250 } 251 252 static const GURL& GetDocumentURL(const ContextMenuParams& params) { 253 return params.frame_url.is_empty() ? params.page_url : params.frame_url; 254 } 255 256 // Given a list of items, returns the ones that match given the contents 257 // of |params| and the profile. 258 static ExtensionMenuItem::List GetRelevantExtensionItems( 259 const ExtensionMenuItem::List& items, 260 const ContextMenuParams& params, 261 Profile* profile, 262 bool can_cross_incognito) { 263 ExtensionMenuItem::List result; 264 for (ExtensionMenuItem::List::const_iterator i = items.begin(); 265 i != items.end(); ++i) { 266 const ExtensionMenuItem* item = *i; 267 268 if (!ExtensionContextMatch(params, item->contexts())) 269 continue; 270 271 const GURL& document_url = GetDocumentURL(params); 272 if (!ExtensionPatternMatch(item->document_url_patterns(), document_url)) 273 continue; 274 275 const GURL& target_url = 276 params.src_url.is_empty() ? params.link_url : params.src_url; 277 if (!ExtensionPatternMatch(item->target_url_patterns(), target_url)) 278 continue; 279 280 if (item->id().profile == profile || can_cross_incognito) 281 result.push_back(*i); 282 } 283 return result; 284 } 285 286 void RenderViewContextMenu::AppendExtensionItems( 287 const std::string& extension_id, int* index) { 288 ExtensionService* service = profile_->GetExtensionService(); 289 ExtensionMenuManager* manager = service->menu_manager(); 290 const Extension* extension = service->GetExtensionById(extension_id, false); 291 DCHECK_GE(*index, 0); 292 int max_index = 293 IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; 294 if (!extension || *index >= max_index) 295 return; 296 297 // Find matching items. 298 const ExtensionMenuItem::List* all_items = manager->MenuItems(extension_id); 299 if (!all_items || all_items->empty()) 300 return; 301 bool can_cross_incognito = service->CanCrossIncognito(extension); 302 ExtensionMenuItem::List items = 303 GetRelevantExtensionItems(*all_items, params_, profile_, 304 can_cross_incognito); 305 if (items.empty()) 306 return; 307 308 // If this is the first extension-provided menu item, add a separator. 309 if (*index == 0) 310 menu_model_.AddSeparator(); 311 312 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; 313 314 // Extensions are only allowed one top-level slot (and it can't be a radio or 315 // checkbox item because we are going to put the extension icon next to it). 316 // If they have more than that, we automatically push them into a submenu. 317 string16 title; 318 ExtensionMenuItem::List submenu_items; 319 if (items.size() > 1 || items[0]->type() != ExtensionMenuItem::NORMAL) { 320 title = UTF8ToUTF16(extension->name()); 321 submenu_items = items; 322 } else { 323 ExtensionMenuItem* item = items[0]; 324 extension_item_map_[menu_id] = item->id(); 325 title = item->TitleWithReplacement(PrintableSelectionText(), 326 kMaxExtensionItemTitleLength); 327 submenu_items = GetRelevantExtensionItems(item->children(), params_, 328 profile_, can_cross_incognito); 329 } 330 331 // Now add our item(s) to the menu_model_. 332 if (submenu_items.empty()) { 333 menu_model_.AddItem(menu_id, title); 334 } else { 335 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); 336 extension_menu_models_.push_back(submenu); 337 menu_model_.AddSubMenu(menu_id, title, submenu); 338 RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, submenu, 339 index); 340 } 341 SetExtensionIcon(extension_id); 342 } 343 344 void RenderViewContextMenu::RecursivelyAppendExtensionItems( 345 const ExtensionMenuItem::List& items, 346 bool can_cross_incognito, 347 ui::SimpleMenuModel* menu_model, 348 int *index) { 349 string16 selection_text = PrintableSelectionText(); 350 ExtensionMenuItem::Type last_type = ExtensionMenuItem::NORMAL; 351 int radio_group_id = 1; 352 353 for (ExtensionMenuItem::List::const_iterator i = items.begin(); 354 i != items.end(); ++i) { 355 ExtensionMenuItem* item = *i; 356 357 // If last item was of type radio but the current one isn't, auto-insert 358 // a separator. The converse case is handled below. 359 if (last_type == ExtensionMenuItem::RADIO && 360 item->type() != ExtensionMenuItem::RADIO) { 361 menu_model->AddSeparator(); 362 last_type = ExtensionMenuItem::SEPARATOR; 363 } 364 365 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; 366 if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) 367 return; 368 extension_item_map_[menu_id] = item->id(); 369 string16 title = item->TitleWithReplacement(selection_text, 370 kMaxExtensionItemTitleLength); 371 if (item->type() == ExtensionMenuItem::NORMAL) { 372 ExtensionMenuItem::List children = 373 GetRelevantExtensionItems(item->children(), params_, 374 profile_, can_cross_incognito); 375 if (children.empty()) { 376 menu_model->AddItem(menu_id, title); 377 } else { 378 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); 379 extension_menu_models_.push_back(submenu); 380 menu_model->AddSubMenu(menu_id, title, submenu); 381 RecursivelyAppendExtensionItems(children, can_cross_incognito, 382 submenu, index); 383 } 384 } else if (item->type() == ExtensionMenuItem::CHECKBOX) { 385 menu_model->AddCheckItem(menu_id, title); 386 } else if (item->type() == ExtensionMenuItem::RADIO) { 387 if (i != items.begin() && 388 last_type != ExtensionMenuItem::RADIO) { 389 radio_group_id++; 390 391 // Auto-append a separator if needed. 392 if (last_type != ExtensionMenuItem::SEPARATOR) 393 menu_model->AddSeparator(); 394 } 395 396 menu_model->AddRadioItem(menu_id, title, radio_group_id); 397 } else if (item->type() == ExtensionMenuItem::SEPARATOR) { 398 if (i != items.begin() && last_type != ExtensionMenuItem::SEPARATOR) { 399 menu_model->AddSeparator(); 400 } 401 } 402 last_type = item->type(); 403 } 404 } 405 406 void RenderViewContextMenu::SetExtensionIcon(const std::string& extension_id) { 407 ExtensionService* service = profile_->GetExtensionService(); 408 ExtensionMenuManager* menu_manager = service->menu_manager(); 409 410 int index = menu_model_.GetItemCount() - 1; 411 DCHECK_GE(index, 0); 412 413 const SkBitmap& icon = menu_manager->GetIconForExtension(extension_id); 414 DCHECK(icon.width() == kFaviconSize); 415 DCHECK(icon.height() == kFaviconSize); 416 417 menu_model_.SetIcon(index, icon); 418 } 419 420 void RenderViewContextMenu::AppendAllExtensionItems() { 421 extension_item_map_.clear(); 422 ExtensionService* service = profile_->GetExtensionService(); 423 if (!service) 424 return; // In unit-tests, we may not have an ExtensionService. 425 ExtensionMenuManager* menu_manager = service->menu_manager(); 426 const GURL& document_url = GetDocumentURL(params_); 427 if (!menu_manager->HasAllowedScheme(document_url)) 428 return; 429 430 // Get a list of extension id's that have context menu items, and sort it by 431 // the extension's name. 432 std::set<std::string> ids = menu_manager->ExtensionIds(); 433 std::vector<std::pair<std::string, std::string> > sorted_ids; 434 for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) { 435 const Extension* extension = service->GetExtensionById(*i, false); 436 if (extension) 437 sorted_ids.push_back( 438 std::pair<std::string, std::string>(extension->name(), *i)); 439 } 440 // TODO(asargent) - See if this works properly for i18n names (bug 32363). 441 std::sort(sorted_ids.begin(), sorted_ids.end()); 442 443 if (sorted_ids.empty()) 444 return; 445 446 int index = 0; 447 base::TimeTicks begin = base::TimeTicks::Now(); 448 std::vector<std::pair<std::string, std::string> >::const_iterator i; 449 for (i = sorted_ids.begin(); 450 i != sorted_ids.end(); ++i) { 451 AppendExtensionItems(i->second, &index); 452 } 453 UMA_HISTOGRAM_TIMES("Extensions.ContextMenus_BuildTime", 454 base::TimeTicks::Now() - begin); 455 UMA_HISTOGRAM_COUNTS("Extensions.ContextMenus_ItemCount", index); 456 } 457 458 void RenderViewContextMenu::InitMenu() { 459 bool has_link = !params_.link_url.is_empty(); 460 bool has_selection = !params_.selection_text.empty(); 461 462 if (AppendCustomItems()) { 463 // Don't add items for Pepper menu. 464 if (!params_.custom_context.is_pepper_menu) 465 AppendDeveloperItems(); 466 return; 467 } 468 469 // When no special node or text is selected and selection has no link, 470 // show page items. 471 bool is_devtools = false; 472 if (params_.media_type == WebContextMenuData::MediaTypeNone && 473 !has_link && 474 !params_.is_editable && 475 !has_selection) { 476 if (!params_.page_url.is_empty()) { 477 is_devtools = IsDevToolsURL(params_.page_url); 478 if (!is_devtools && !IsInternalResourcesURL(params_.page_url)) { 479 AppendPageItems(); 480 // Merge in frame items if we clicked within a frame that needs them. 481 if (!params_.frame_url.is_empty()) { 482 is_devtools = IsDevToolsURL(params_.frame_url); 483 if (!is_devtools && !IsInternalResourcesURL(params_.frame_url)) { 484 menu_model_.AddSeparator(); 485 AppendFrameItems(); 486 } 487 } 488 } 489 } else { 490 DCHECK(params_.frame_url.is_empty()); 491 } 492 } 493 494 if (has_link) { 495 AppendLinkItems(); 496 if (params_.media_type != WebContextMenuData::MediaTypeNone) 497 menu_model_.AddSeparator(); 498 } 499 500 switch (params_.media_type) { 501 case WebContextMenuData::MediaTypeNone: 502 break; 503 case WebContextMenuData::MediaTypeImage: 504 AppendImageItems(); 505 break; 506 case WebContextMenuData::MediaTypeVideo: 507 AppendVideoItems(); 508 break; 509 case WebContextMenuData::MediaTypeAudio: 510 AppendAudioItems(); 511 break; 512 case WebContextMenuData::MediaTypePlugin: 513 AppendPluginItems(); 514 break; 515 #ifdef WEBCONTEXT_MEDIATYPEFILE_DEFINED 516 case WebContextMenuData::MediaTypeFile: 517 break; 518 #endif 519 } 520 521 if (params_.is_editable) 522 AppendEditableItems(); 523 else if (has_selection) 524 AppendCopyItem(); 525 526 if (has_selection) 527 AppendSearchProvider(); 528 529 if (!is_devtools) 530 AppendAllExtensionItems(); 531 532 AppendDeveloperItems(); 533 } 534 535 void RenderViewContextMenu::LookUpInDictionary() { 536 // Used only in the Mac port. 537 NOTREACHED(); 538 } 539 540 bool RenderViewContextMenu::AppendCustomItems() { 541 size_t total_items = 0; 542 AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this, 543 &menu_model_); 544 return total_items > 0; 545 } 546 547 void RenderViewContextMenu::AppendDeveloperItems() { 548 // In the DevTools popup menu, "developer items" is normally the only 549 // section, so omit the separator there. 550 if (menu_model_.GetItemCount() > 0) 551 menu_model_.AddSeparator(); 552 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTELEMENT, 553 IDS_CONTENT_CONTEXT_INSPECTELEMENT); 554 } 555 556 void RenderViewContextMenu::AppendLinkItems() { 557 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB, 558 IDS_CONTENT_CONTEXT_OPENLINKNEWTAB); 559 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW, 560 IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW); 561 if (!external_) { 562 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD, 563 IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD); 564 } 565 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVELINKAS, 566 IDS_CONTENT_CONTEXT_SAVELINKAS); 567 568 menu_model_.AddItemWithStringId( 569 IDC_CONTENT_CONTEXT_COPYLINKLOCATION, 570 params_.link_url.SchemeIs(chrome::kMailToScheme) ? 571 IDS_CONTENT_CONTEXT_COPYEMAILADDRESS : 572 IDS_CONTENT_CONTEXT_COPYLINKLOCATION); 573 } 574 575 void RenderViewContextMenu::AppendImageItems() { 576 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 577 IDS_CONTENT_CONTEXT_SAVEIMAGEAS); 578 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION, 579 IDS_CONTENT_CONTEXT_COPYIMAGELOCATION); 580 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGE, 581 IDS_CONTENT_CONTEXT_COPYIMAGE); 582 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB, 583 IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB); 584 } 585 586 void RenderViewContextMenu::AppendAudioItems() { 587 AppendMediaItems(); 588 menu_model_.AddSeparator(); 589 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, 590 IDS_CONTENT_CONTEXT_SAVEAUDIOAS); 591 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, 592 IDS_CONTENT_CONTEXT_COPYAUDIOLOCATION); 593 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, 594 IDS_CONTENT_CONTEXT_OPENAUDIONEWTAB); 595 } 596 597 void RenderViewContextMenu::AppendVideoItems() { 598 AppendMediaItems(); 599 menu_model_.AddSeparator(); 600 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, 601 IDS_CONTENT_CONTEXT_SAVEVIDEOAS); 602 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, 603 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION); 604 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, 605 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB); 606 } 607 608 void RenderViewContextMenu::AppendMediaItems() { 609 int media_flags = params_.media_flags; 610 611 menu_model_.AddItemWithStringId( 612 IDC_CONTENT_CONTEXT_PLAYPAUSE, 613 media_flags & WebContextMenuData::MediaPaused ? 614 IDS_CONTENT_CONTEXT_PLAY : 615 IDS_CONTENT_CONTEXT_PAUSE); 616 617 menu_model_.AddItemWithStringId( 618 IDC_CONTENT_CONTEXT_MUTE, 619 media_flags & WebContextMenuData::MediaMuted ? 620 IDS_CONTENT_CONTEXT_UNMUTE : 621 IDS_CONTENT_CONTEXT_MUTE); 622 623 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_LOOP, 624 IDS_CONTENT_CONTEXT_LOOP); 625 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_CONTROLS, 626 IDS_CONTENT_CONTEXT_CONTROLS); 627 } 628 629 void RenderViewContextMenu::AppendPluginItems() { 630 if (params_.page_url == params_.src_url) { 631 // Full page plugin, so show page menu items. 632 if (params_.link_url.is_empty() && params_.selection_text.empty()) 633 AppendPageItems(); 634 } else { 635 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, 636 IDS_CONTENT_CONTEXT_SAVEPAGEAS); 637 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT); 638 } 639 } 640 641 void RenderViewContextMenu::AppendPageItems() { 642 menu_model_.AddItemWithStringId(IDC_BACK, IDS_CONTENT_CONTEXT_BACK); 643 menu_model_.AddItemWithStringId(IDC_FORWARD, IDS_CONTENT_CONTEXT_FORWARD); 644 menu_model_.AddItemWithStringId(IDC_RELOAD, IDS_CONTENT_CONTEXT_RELOAD); 645 menu_model_.AddSeparator(); 646 menu_model_.AddItemWithStringId(IDC_SAVE_PAGE, 647 IDS_CONTENT_CONTEXT_SAVEPAGEAS); 648 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT); 649 650 std::string locale = g_browser_process->GetApplicationLocale(); 651 locale = TranslateManager::GetLanguageCode(locale); 652 string16 language = l10n_util::GetDisplayNameForLocale(locale, locale, true); 653 menu_model_.AddItem( 654 IDC_CONTENT_CONTEXT_TRANSLATE, 655 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE, language)); 656 657 menu_model_.AddItemWithStringId(IDC_VIEW_SOURCE, 658 IDS_CONTENT_CONTEXT_VIEWPAGESOURCE); 659 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWPAGEINFO, 660 IDS_CONTENT_CONTEXT_VIEWPAGEINFO); 661 } 662 663 void RenderViewContextMenu::AppendFrameItems() { 664 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOADFRAME, 665 IDS_CONTENT_CONTEXT_RELOADFRAME); 666 // These two menu items have yet to be implemented. 667 // http://code.google.com/p/chromium/issues/detail?id=11827 668 // IDS_CONTENT_CONTEXT_SAVEFRAMEAS 669 // IDS_CONTENT_CONTEXT_PRINTFRAME 670 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE, 671 IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE); 672 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMEINFO, 673 IDS_CONTENT_CONTEXT_VIEWFRAMEINFO); 674 } 675 676 void RenderViewContextMenu::AppendCopyItem() { 677 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY, 678 IDS_CONTENT_CONTEXT_COPY); 679 } 680 681 void RenderViewContextMenu::AppendSearchProvider() { 682 DCHECK(profile_); 683 684 TrimWhitespace(params_.selection_text, TRIM_ALL, ¶ms_.selection_text); 685 if (params_.selection_text.empty()) 686 return; 687 688 AutocompleteMatch match; 689 profile_->GetAutocompleteClassifier()->Classify( 690 params_.selection_text, string16(), false, &match, NULL); 691 selection_navigation_url_ = match.destination_url; 692 if (!selection_navigation_url_.is_valid()) 693 return; 694 695 string16 printable_selection_text = PrintableSelectionText(); 696 // Escape "&" as "&&". 697 for (size_t i = printable_selection_text.find('&'); i != string16::npos; 698 i = printable_selection_text.find('&', i + 2)) 699 printable_selection_text.insert(i, 1, '&'); 700 701 if (match.transition == PageTransition::TYPED) { 702 if (ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme( 703 selection_navigation_url_.scheme())) { 704 menu_model_.AddItem( 705 IDC_CONTENT_CONTEXT_GOTOURL, 706 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_GOTOURL, 707 printable_selection_text)); 708 } 709 } else { 710 const TemplateURL* const default_provider = 711 profile_->GetTemplateURLModel()->GetDefaultSearchProvider(); 712 if (!default_provider) 713 return; 714 menu_model_.AddItem( 715 IDC_CONTENT_CONTEXT_SEARCHWEBFOR, 716 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR, 717 default_provider->short_name(), 718 printable_selection_text)); 719 } 720 } 721 722 void RenderViewContextMenu::AppendEditableItems() { 723 // Append Dictionary spell check suggestions. 724 for (size_t i = 0; i < params_.dictionary_suggestions.size() && 725 IDC_SPELLCHECK_SUGGESTION_0 + i <= IDC_SPELLCHECK_SUGGESTION_LAST; 726 ++i) { 727 menu_model_.AddItem(IDC_SPELLCHECK_SUGGESTION_0 + static_cast<int>(i), 728 params_.dictionary_suggestions[i]); 729 } 730 if (!params_.dictionary_suggestions.empty()) 731 menu_model_.AddSeparator(); 732 733 // If word is misspelled, give option for "Add to dictionary" 734 if (!params_.misspelled_word.empty()) { 735 if (params_.dictionary_suggestions.empty()) { 736 menu_model_.AddItem(0, 737 l10n_util::GetStringUTF16( 738 IDS_CONTENT_CONTEXT_NO_SPELLING_SUGGESTIONS)); 739 } 740 menu_model_.AddItemWithStringId(IDC_SPELLCHECK_ADD_TO_DICTIONARY, 741 IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY); 742 menu_model_.AddSeparator(); 743 } 744 745 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO, 746 IDS_CONTENT_CONTEXT_UNDO); 747 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO, 748 IDS_CONTENT_CONTEXT_REDO); 749 menu_model_.AddSeparator(); 750 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_CUT, 751 IDS_CONTENT_CONTEXT_CUT); 752 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY, 753 IDS_CONTENT_CONTEXT_COPY); 754 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE, 755 IDS_CONTENT_CONTEXT_PASTE); 756 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_DELETE, 757 IDS_CONTENT_CONTEXT_DELETE); 758 menu_model_.AddSeparator(); 759 760 AppendSpellcheckOptionsSubMenu(); 761 762 #if defined(OS_MACOSX) 763 // OS X provides a contextual menu to set writing direction for BiDi 764 // languages. 765 // This functionality is exposed as a keyboard shortcut on Windows & Linux. 766 AppendBidiSubMenu(); 767 #endif // OS_MACOSX 768 769 menu_model_.AddSeparator(); 770 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SELECTALL, 771 IDS_CONTENT_CONTEXT_SELECTALL); 772 } 773 774 void RenderViewContextMenu::AppendSpellcheckOptionsSubMenu() { 775 // Add Spell Check languages to sub menu. 776 std::vector<std::string> spellcheck_languages; 777 SpellCheckHost::GetSpellCheckLanguages(profile_, 778 &spellcheck_languages); 779 DCHECK(spellcheck_languages.size() < 780 IDC_SPELLCHECK_LANGUAGES_LAST - IDC_SPELLCHECK_LANGUAGES_FIRST); 781 const std::string app_locale = g_browser_process->GetApplicationLocale(); 782 for (size_t i = 0; i < spellcheck_languages.size(); ++i) { 783 string16 display_name(l10n_util::GetDisplayNameForLocale( 784 spellcheck_languages[i], app_locale, true)); 785 spellcheck_submenu_model_.AddRadioItem( 786 IDC_SPELLCHECK_LANGUAGES_FIRST + i, 787 display_name, 788 kSpellcheckRadioGroup); 789 } 790 791 // Add item in the sub menu to pop up the fonts and languages options menu. 792 spellcheck_submenu_model_.AddSeparator(); 793 spellcheck_submenu_model_.AddItemWithStringId( 794 IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS, 795 IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS); 796 797 // Add 'Check the spelling of this field' item in the sub menu. 798 spellcheck_submenu_model_.AddCheckItem( 799 IDC_CHECK_SPELLING_OF_THIS_FIELD, 800 l10n_util::GetStringUTF16( 801 IDS_CONTENT_CONTEXT_CHECK_SPELLING_OF_THIS_FIELD)); 802 803 // Add option for showing the spelling panel if the platform spellchecker 804 // supports it. 805 if (SpellCheckerPlatform::SpellCheckerAvailable() && 806 SpellCheckerPlatform::SpellCheckerProvidesPanel()) { 807 spellcheck_submenu_model_.AddCheckItem( 808 IDC_SPELLPANEL_TOGGLE, 809 l10n_util::GetStringUTF16( 810 SpellCheckerPlatform::SpellingPanelVisible() ? 811 IDS_CONTENT_CONTEXT_HIDE_SPELLING_PANEL : 812 IDS_CONTENT_CONTEXT_SHOW_SPELLING_PANEL)); 813 } 814 815 menu_model_.AddSubMenu( 816 IDC_SPELLCHECK_MENU, 817 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPELLCHECK_MENU), 818 &spellcheck_submenu_model_); 819 } 820 821 #if defined(OS_MACOSX) 822 void RenderViewContextMenu::AppendBidiSubMenu() { 823 bidi_submenu_model_.AddCheckItem(IDC_WRITING_DIRECTION_DEFAULT, 824 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT)); 825 bidi_submenu_model_.AddCheckItem(IDC_WRITING_DIRECTION_LTR, 826 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR)); 827 bidi_submenu_model_.AddCheckItem(IDC_WRITING_DIRECTION_RTL, 828 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL)); 829 830 menu_model_.AddSubMenu( 831 IDC_WRITING_DIRECTION_MENU, 832 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU), 833 &bidi_submenu_model_); 834 } 835 #endif // OS_MACOSX 836 837 ExtensionMenuItem* RenderViewContextMenu::GetExtensionMenuItem(int id) const { 838 ExtensionMenuManager* manager = 839 profile_->GetExtensionService()->menu_manager(); 840 std::map<int, ExtensionMenuItem::Id>::const_iterator i = 841 extension_item_map_.find(id); 842 if (i != extension_item_map_.end()) { 843 ExtensionMenuItem* item = manager->GetItemById(i->second); 844 if (item) 845 return item; 846 } 847 return NULL; 848 } 849 850 // Menu delegate functions ----------------------------------------------------- 851 852 bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { 853 if (id == IDC_PRINT && 854 (source_tab_contents_->content_restrictions() & 855 CONTENT_RESTRICTION_PRINT)) { 856 return false; 857 } 858 859 if (id == IDC_SAVE_PAGE && 860 (source_tab_contents_->content_restrictions() & 861 CONTENT_RESTRICTION_SAVE)) { 862 return false; 863 } 864 865 // Allow Spell Check language items on sub menu for text area context menu. 866 if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) && 867 (id < IDC_SPELLCHECK_LANGUAGES_LAST)) { 868 return profile_->GetPrefs()->GetBoolean(prefs::kEnableSpellCheck); 869 } 870 871 // Custom items. 872 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && 873 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { 874 return IsCustomItemEnabled(params_.custom_items, id); 875 } 876 877 // Extension items. 878 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && 879 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { 880 // In the future we may add APIs for extensions to disable items, but for 881 // now all items are implicitly enabled. 882 return true; 883 } 884 885 switch (id) { 886 case IDC_BACK: 887 return source_tab_contents_->controller().CanGoBack(); 888 889 case IDC_FORWARD: 890 return source_tab_contents_->controller().CanGoForward(); 891 892 case IDC_RELOAD: 893 return source_tab_contents_->delegate() && 894 source_tab_contents_->delegate()->CanReloadContents( 895 source_tab_contents_); 896 897 case IDC_VIEW_SOURCE: 898 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE: 899 return source_tab_contents_->controller().CanViewSource(); 900 901 case IDC_CONTENT_CONTEXT_INSPECTELEMENT: 902 // Viewing page info is not a developer command but is meaningful for the 903 // same set of pages which developer commands are meaningful for. 904 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: 905 return IsDevCommandEnabled(id); 906 907 case IDC_CONTENT_CONTEXT_TRANSLATE: { 908 TranslateTabHelper* helper = 909 TabContentsWrapper::GetCurrentWrapperForContents( 910 source_tab_contents_)->translate_tab_helper(); 911 std::string original_lang = 912 helper->language_state().original_language(); 913 std::string target_lang = g_browser_process->GetApplicationLocale(); 914 target_lang = TranslateManager::GetLanguageCode(target_lang); 915 // Note that we intentionally enable the menu even if the original and 916 // target languages are identical. This is to give a way to user to 917 // translate a page that might contains text fragments in a different 918 // language. 919 return !!(params_.edit_flags & WebContextMenuData::CanTranslate) && 920 helper->language_state().page_translatable() && 921 !original_lang.empty() && // Did we receive the page language yet? 922 // Only allow translating languages we explitly support and the 923 // unknown language (in which case the page language is detected on 924 // the server side). 925 (original_lang == chrome::kUnknownLanguageCode || 926 TranslateManager::IsSupportedLanguage(original_lang)) && 927 !helper->language_state().IsPageTranslated() && 928 !source_tab_contents_->interstitial_page() && 929 TranslateManager::IsTranslatableURL(params_.page_url); 930 } 931 932 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: 933 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW: 934 return params_.link_url.is_valid(); 935 936 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION: 937 return params_.unfiltered_link_url.is_valid(); 938 939 case IDC_CONTENT_CONTEXT_SAVELINKAS: 940 return params_.link_url.is_valid() && 941 net::URLRequest::IsHandledURL(params_.link_url); 942 943 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: 944 return params_.src_url.is_valid() && 945 net::URLRequest::IsHandledURL(params_.src_url); 946 947 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB: 948 // The images shown in the most visited thumbnails do not currently open 949 // in a new tab as they should. Disabling this context menu option for 950 // now, as a quick hack, before we resolve this issue (Issue = 2608). 951 // TODO(sidchat): Enable this option once this issue is resolved. 952 if (params_.src_url.scheme() == chrome::kChromeUIScheme || 953 !params_.src_url.is_valid()) 954 return false; 955 return true; 956 957 case IDC_CONTENT_CONTEXT_COPYIMAGE: 958 return !params_.is_image_blocked; 959 960 // Media control commands should all be disabled if the player is in an 961 // error state. 962 case IDC_CONTENT_CONTEXT_PLAYPAUSE: 963 case IDC_CONTENT_CONTEXT_LOOP: 964 return (params_.media_flags & 965 WebContextMenuData::MediaInError) == 0; 966 967 // Mute and unmute should also be disabled if the player has no audio. 968 case IDC_CONTENT_CONTEXT_MUTE: 969 return (params_.media_flags & 970 WebContextMenuData::MediaHasAudio) != 0 && 971 (params_.media_flags & 972 WebContextMenuData::MediaInError) == 0; 973 974 // Media controls can be toggled only for video player. If we toggle 975 // controls for audio then the player disappears, and there is no way to 976 // return it back. 977 case IDC_CONTENT_CONTEXT_CONTROLS: 978 return (params_.media_flags & 979 WebContextMenuData::MediaHasVideo) != 0; 980 981 case IDC_CONTENT_CONTEXT_COPYAVLOCATION: 982 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION: 983 return params_.src_url.is_valid(); 984 985 case IDC_CONTENT_CONTEXT_SAVEAVAS: 986 return (params_.media_flags & 987 WebContextMenuData::MediaCanSave) && 988 params_.src_url.is_valid() && 989 net::URLRequest::IsHandledURL(params_.src_url); 990 991 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB: 992 return true; 993 994 case IDC_SAVE_PAGE: { 995 // Instead of using GetURL here, we use url() (which is the "real" url of 996 // the page) from the NavigationEntry because its reflects their origin 997 // rather than the display one (returned by GetURL) which may be 998 // different (like having "view-source:" on the front). 999 NavigationEntry* active_entry = 1000 source_tab_contents_->controller().GetActiveEntry(); 1001 return SavePackage::IsSavableURL( 1002 (active_entry) ? active_entry->url() : GURL()); 1003 } 1004 1005 case IDC_CONTENT_CONTEXT_RELOADFRAME: 1006 return params_.frame_url.is_valid(); 1007 1008 case IDC_CONTENT_CONTEXT_UNDO: 1009 return !!(params_.edit_flags & WebContextMenuData::CanUndo); 1010 1011 case IDC_CONTENT_CONTEXT_REDO: 1012 return !!(params_.edit_flags & WebContextMenuData::CanRedo); 1013 1014 case IDC_CONTENT_CONTEXT_CUT: 1015 return !!(params_.edit_flags & WebContextMenuData::CanCut); 1016 1017 case IDC_CONTENT_CONTEXT_COPY: 1018 return !!(params_.edit_flags & WebContextMenuData::CanCopy); 1019 1020 case IDC_CONTENT_CONTEXT_PASTE: 1021 return !!(params_.edit_flags & WebContextMenuData::CanPaste); 1022 1023 case IDC_CONTENT_CONTEXT_DELETE: 1024 return !!(params_.edit_flags & WebContextMenuData::CanDelete); 1025 1026 case IDC_CONTENT_CONTEXT_SELECTALL: 1027 return !!(params_.edit_flags & WebContextMenuData::CanSelectAll); 1028 1029 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD: 1030 return !profile_->IsOffTheRecord() && params_.link_url.is_valid() && 1031 profile_->GetPrefs()->GetBoolean(prefs::kIncognitoEnabled); 1032 1033 case IDC_SPELLCHECK_ADD_TO_DICTIONARY: 1034 return !params_.misspelled_word.empty(); 1035 1036 case IDC_PRINT: 1037 if (g_browser_process->local_state() && 1038 !g_browser_process->local_state()->GetBoolean( 1039 prefs::kPrintingEnabled)) { 1040 return false; 1041 } 1042 return params_.media_type == WebContextMenuData::MediaTypeNone || 1043 params_.media_flags & WebContextMenuData::MediaCanPrint; 1044 1045 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR: 1046 case IDC_CONTENT_CONTEXT_GOTOURL: 1047 case IDC_SPELLCHECK_SUGGESTION_0: 1048 case IDC_SPELLCHECK_SUGGESTION_1: 1049 case IDC_SPELLCHECK_SUGGESTION_2: 1050 case IDC_SPELLCHECK_SUGGESTION_3: 1051 case IDC_SPELLCHECK_SUGGESTION_4: 1052 case IDC_SPELLPANEL_TOGGLE: 1053 #if !defined(OS_MACOSX) 1054 // TODO(jeremy): re-enable - http://crbug.com/34512 . 1055 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS: 1056 #endif 1057 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO: 1058 return true; 1059 1060 case IDC_CHECK_SPELLING_OF_THIS_FIELD: 1061 return profile_->GetPrefs()->GetBoolean(prefs::kEnableSpellCheck); 1062 1063 #if defined(OS_MACOSX) 1064 // TODO(jeremy): re-enable - http://crbug.com/34512 . 1065 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS: 1066 return false; 1067 #endif 1068 1069 #if defined(OS_MACOSX) 1070 case IDC_WRITING_DIRECTION_DEFAULT: // Provided to match OS defaults. 1071 return params_.writing_direction_default & 1072 WebContextMenuData::CheckableMenuItemEnabled; 1073 case IDC_WRITING_DIRECTION_RTL: 1074 return params_.writing_direction_right_to_left & 1075 WebContextMenuData::CheckableMenuItemEnabled; 1076 case IDC_WRITING_DIRECTION_LTR: 1077 return params_.writing_direction_left_to_right & 1078 WebContextMenuData::CheckableMenuItemEnabled; 1079 case IDC_WRITING_DIRECTION_MENU: 1080 return true; 1081 case IDC_CONTENT_CONTEXT_LOOK_UP_IN_DICTIONARY: 1082 // This is OK because the menu is not shown when it isn't 1083 // appropriate. 1084 return true; 1085 #elif defined(OS_POSIX) 1086 // TODO(suzhe): this should not be enabled for password fields. 1087 case IDC_INPUT_METHODS_MENU: 1088 return true; 1089 #endif 1090 1091 case IDC_SPELLCHECK_MENU: 1092 return true; 1093 1094 default: 1095 NOTREACHED(); 1096 return false; 1097 } 1098 } 1099 1100 bool RenderViewContextMenu::IsCommandIdChecked(int id) const { 1101 // See if the video is set to looping. 1102 if (id == IDC_CONTENT_CONTEXT_LOOP) { 1103 return (params_.media_flags & 1104 WebContextMenuData::MediaLoop) != 0; 1105 } 1106 1107 if (id == IDC_CONTENT_CONTEXT_CONTROLS) { 1108 return (params_.media_flags & 1109 WebContextMenuData::MediaControlRootElement) != 0; 1110 } 1111 1112 // Custom items. 1113 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && 1114 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { 1115 return IsCustomItemChecked(params_.custom_items, id); 1116 } 1117 1118 // Extension items. 1119 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && 1120 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { 1121 ExtensionMenuItem* item = GetExtensionMenuItem(id); 1122 if (item) 1123 return item->checked(); 1124 else 1125 return false; 1126 } 1127 1128 #if defined(OS_MACOSX) 1129 if (id == IDC_WRITING_DIRECTION_DEFAULT) 1130 return params_.writing_direction_default & 1131 WebContextMenuData::CheckableMenuItemChecked; 1132 if (id == IDC_WRITING_DIRECTION_RTL) 1133 return params_.writing_direction_right_to_left & 1134 WebContextMenuData::CheckableMenuItemChecked; 1135 if (id == IDC_WRITING_DIRECTION_LTR) 1136 return params_.writing_direction_left_to_right & 1137 WebContextMenuData::CheckableMenuItemChecked; 1138 if (id == IDC_CONTENT_CONTEXT_LOOK_UP_IN_DICTIONARY) 1139 return false; 1140 #endif // OS_MACOSX 1141 1142 // Check box for 'Check the Spelling of this field'. 1143 if (id == IDC_CHECK_SPELLING_OF_THIS_FIELD) { 1144 return (params_.spellcheck_enabled && 1145 profile_->GetPrefs()->GetBoolean(prefs::kEnableSpellCheck)); 1146 } 1147 1148 // Don't bother getting the display language vector if this isn't a spellcheck 1149 // language. 1150 if ((id < IDC_SPELLCHECK_LANGUAGES_FIRST) || 1151 (id >= IDC_SPELLCHECK_LANGUAGES_LAST)) 1152 return false; 1153 1154 std::vector<std::string> languages; 1155 return SpellCheckHost::GetSpellCheckLanguages(profile_, &languages) == 1156 (id - IDC_SPELLCHECK_LANGUAGES_FIRST); 1157 } 1158 1159 void RenderViewContextMenu::ExecuteCommand(int id) { 1160 // Check to see if one of the spell check language ids have been clicked. 1161 if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST && 1162 id < IDC_SPELLCHECK_LANGUAGES_LAST) { 1163 const size_t language_number = id - IDC_SPELLCHECK_LANGUAGES_FIRST; 1164 std::vector<std::string> languages; 1165 SpellCheckHost::GetSpellCheckLanguages(profile_, &languages); 1166 if (language_number < languages.size()) { 1167 StringPrefMember dictionary_language; 1168 dictionary_language.Init(prefs::kSpellCheckDictionary, 1169 profile_->GetPrefs(), NULL); 1170 dictionary_language.SetValue(languages[language_number]); 1171 } 1172 return; 1173 } 1174 1175 // Process custom actions range. 1176 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && 1177 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { 1178 unsigned action = id - IDC_CONTENT_CONTEXT_CUSTOM_FIRST; 1179 source_tab_contents_->render_view_host()->PerformCustomContextMenuAction( 1180 params_.custom_context, action); 1181 return; 1182 } 1183 1184 // Process extension menu items. 1185 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && 1186 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { 1187 ExtensionMenuManager* manager = 1188 profile_->GetExtensionService()->menu_manager(); 1189 std::map<int, ExtensionMenuItem::Id>::const_iterator i = 1190 extension_item_map_.find(id); 1191 if (i != extension_item_map_.end()) { 1192 manager->ExecuteCommand(profile_, source_tab_contents_, params_, 1193 i->second); 1194 } 1195 return; 1196 } 1197 1198 1199 switch (id) { 1200 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: 1201 OpenURL(params_.link_url, 1202 source_tab_contents_->delegate() && 1203 source_tab_contents_->delegate()->IsApplication() ? 1204 NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB, 1205 PageTransition::LINK); 1206 break; 1207 1208 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW: 1209 OpenURL(params_.link_url, NEW_WINDOW, PageTransition::LINK); 1210 break; 1211 1212 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD: 1213 OpenURL(params_.link_url, OFF_THE_RECORD, PageTransition::LINK); 1214 break; 1215 1216 case IDC_CONTENT_CONTEXT_SAVEAVAS: 1217 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: 1218 case IDC_CONTENT_CONTEXT_SAVELINKAS: { 1219 download_util::RecordDownloadCount( 1220 download_util::INITIATED_BY_CONTEXT_MENU_COUNT); 1221 const GURL& referrer = 1222 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url; 1223 const GURL& url = 1224 (id == IDC_CONTENT_CONTEXT_SAVELINKAS ? params_.link_url : 1225 params_.src_url); 1226 DownloadManager* dlm = profile_->GetDownloadManager(); 1227 dlm->DownloadUrl(url, referrer, params_.frame_charset, 1228 source_tab_contents_); 1229 break; 1230 } 1231 1232 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION: 1233 WriteURLToClipboard(params_.unfiltered_link_url); 1234 break; 1235 1236 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION: 1237 case IDC_CONTENT_CONTEXT_COPYAVLOCATION: 1238 WriteURLToClipboard(params_.src_url); 1239 break; 1240 1241 case IDC_CONTENT_CONTEXT_COPYIMAGE: 1242 CopyImageAt(params_.x, params_.y); 1243 break; 1244 1245 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB: 1246 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB: 1247 OpenURL(params_.src_url, NEW_BACKGROUND_TAB, PageTransition::LINK); 1248 break; 1249 1250 case IDC_CONTENT_CONTEXT_PLAYPAUSE: { 1251 bool play = !!(params_.media_flags & WebContextMenuData::MediaPaused); 1252 if (play) { 1253 UserMetrics::RecordAction(UserMetricsAction("MediaContextMenu_Play"), 1254 profile_); 1255 } else { 1256 UserMetrics::RecordAction(UserMetricsAction("MediaContextMenu_Pause"), 1257 profile_); 1258 } 1259 MediaPlayerActionAt(gfx::Point(params_.x, params_.y), 1260 WebMediaPlayerAction( 1261 WebMediaPlayerAction::Play, play)); 1262 break; 1263 } 1264 1265 case IDC_CONTENT_CONTEXT_MUTE: { 1266 bool mute = !(params_.media_flags & WebContextMenuData::MediaMuted); 1267 if (mute) { 1268 UserMetrics::RecordAction(UserMetricsAction("MediaContextMenu_Mute"), 1269 profile_); 1270 } else { 1271 UserMetrics::RecordAction(UserMetricsAction("MediaContextMenu_Unmute"), 1272 profile_); 1273 } 1274 MediaPlayerActionAt(gfx::Point(params_.x, params_.y), 1275 WebMediaPlayerAction( 1276 WebMediaPlayerAction::Mute, mute)); 1277 break; 1278 } 1279 1280 case IDC_CONTENT_CONTEXT_LOOP: 1281 UserMetrics::RecordAction(UserMetricsAction("MediaContextMenu_Loop"), 1282 profile_); 1283 MediaPlayerActionAt(gfx::Point(params_.x, params_.y), 1284 WebMediaPlayerAction( 1285 WebMediaPlayerAction::Loop, 1286 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP))); 1287 break; 1288 1289 case IDC_CONTENT_CONTEXT_CONTROLS: 1290 UserMetrics::RecordAction(UserMetricsAction("MediaContextMenu_Controls"), 1291 profile_); 1292 MediaPlayerActionAt( 1293 gfx::Point(params_.x, params_.y), 1294 WebMediaPlayerAction( 1295 WebMediaPlayerAction::Controls, 1296 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS))); 1297 break; 1298 1299 case IDC_BACK: 1300 source_tab_contents_->controller().GoBack(); 1301 break; 1302 1303 case IDC_FORWARD: 1304 source_tab_contents_->controller().GoForward(); 1305 break; 1306 1307 case IDC_SAVE_PAGE: { 1308 TabContentsWrapper* wrapper = 1309 TabContentsWrapper::GetCurrentWrapperForContents( 1310 source_tab_contents_); 1311 wrapper->download_tab_helper()->OnSavePage(); 1312 break; 1313 } 1314 1315 case IDC_RELOAD: 1316 // Prevent the modal "Resubmit form post" dialog from appearing in the 1317 // context of an external context menu. 1318 source_tab_contents_->controller().Reload(!external_); 1319 break; 1320 1321 case IDC_PRINT: 1322 if (params_.media_type == WebContextMenuData::MediaTypeNone) { 1323 if (CommandLine::ForCurrentProcess()->HasSwitch( 1324 switches::kEnablePrintPreview)) { 1325 printing::PrintPreviewTabController::PrintPreview( 1326 source_tab_contents_); 1327 } else { 1328 TabContentsWrapper* wrapper = 1329 TabContentsWrapper::GetCurrentWrapperForContents( 1330 source_tab_contents_); 1331 wrapper->print_view_manager()->PrintNow(); 1332 } 1333 } else { 1334 RenderViewHost* rvh = source_tab_contents_->render_view_host(); 1335 rvh->Send(new PrintMsg_PrintNodeUnderContextMenu(rvh->routing_id())); 1336 } 1337 break; 1338 1339 case IDC_VIEW_SOURCE: 1340 source_tab_contents_->ViewSource(); 1341 break; 1342 1343 case IDC_CONTENT_CONTEXT_INSPECTELEMENT: 1344 Inspect(params_.x, params_.y); 1345 break; 1346 1347 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: { 1348 NavigationEntry* nav_entry = 1349 source_tab_contents_->controller().GetActiveEntry(); 1350 source_tab_contents_->ShowPageInfo(nav_entry->url(), nav_entry->ssl(), 1351 true); 1352 break; 1353 } 1354 1355 case IDC_CONTENT_CONTEXT_TRANSLATE: { 1356 // A translation might have been triggered by the time the menu got 1357 // selected, do nothing in that case. 1358 TranslateTabHelper* helper = 1359 TabContentsWrapper::GetCurrentWrapperForContents( 1360 source_tab_contents_)->translate_tab_helper(); 1361 if (helper->language_state().IsPageTranslated() || 1362 helper->language_state().translation_pending()) { 1363 return; 1364 } 1365 std::string original_lang = helper->language_state().original_language(); 1366 std::string target_lang = g_browser_process->GetApplicationLocale(); 1367 target_lang = TranslateManager::GetLanguageCode(target_lang); 1368 // Since the user decided to translate for that language and site, clears 1369 // any preferences for not translating them. 1370 TranslatePrefs prefs(profile_->GetPrefs()); 1371 prefs.RemoveLanguageFromBlacklist(original_lang); 1372 prefs.RemoveSiteFromBlacklist(params_.page_url.HostNoBrackets()); 1373 TranslateManager::GetInstance()->TranslatePage( 1374 source_tab_contents_, original_lang, target_lang); 1375 break; 1376 } 1377 1378 case IDC_CONTENT_CONTEXT_RELOADFRAME: 1379 source_tab_contents_->render_view_host()->ReloadFrame(); 1380 break; 1381 1382 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE: 1383 source_tab_contents_->ViewFrameSource(params_.frame_url, 1384 params_.frame_content_state); 1385 break; 1386 1387 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO: { 1388 // Deserialize the SSL info. 1389 NavigationEntry::SSLStatus ssl; 1390 if (!params_.security_info.empty()) { 1391 int cert_id, cert_status, security_bits, connection_status; 1392 SSLManager::DeserializeSecurityInfo(params_.security_info, 1393 &cert_id, 1394 &cert_status, 1395 &security_bits, 1396 &connection_status); 1397 ssl.set_cert_id(cert_id); 1398 ssl.set_cert_status(cert_status); 1399 ssl.set_security_bits(security_bits); 1400 ssl.set_connection_status(connection_status); 1401 } 1402 source_tab_contents_->ShowPageInfo(params_.frame_url, ssl, 1403 false); // Don't show the history. 1404 break; 1405 } 1406 1407 case IDC_CONTENT_CONTEXT_UNDO: 1408 source_tab_contents_->render_view_host()->Undo(); 1409 break; 1410 1411 case IDC_CONTENT_CONTEXT_REDO: 1412 source_tab_contents_->render_view_host()->Redo(); 1413 break; 1414 1415 case IDC_CONTENT_CONTEXT_CUT: 1416 source_tab_contents_->render_view_host()->Cut(); 1417 break; 1418 1419 case IDC_CONTENT_CONTEXT_COPY: 1420 source_tab_contents_->render_view_host()->Copy(); 1421 break; 1422 1423 case IDC_CONTENT_CONTEXT_PASTE: 1424 source_tab_contents_->render_view_host()->Paste(); 1425 break; 1426 1427 case IDC_CONTENT_CONTEXT_DELETE: 1428 source_tab_contents_->render_view_host()->Delete(); 1429 break; 1430 1431 case IDC_CONTENT_CONTEXT_SELECTALL: 1432 source_tab_contents_->render_view_host()->SelectAll(); 1433 break; 1434 1435 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR: 1436 case IDC_CONTENT_CONTEXT_GOTOURL: { 1437 OpenURL(selection_navigation_url_, NEW_FOREGROUND_TAB, 1438 PageTransition::LINK); 1439 break; 1440 } 1441 1442 case IDC_SPELLCHECK_SUGGESTION_0: 1443 case IDC_SPELLCHECK_SUGGESTION_1: 1444 case IDC_SPELLCHECK_SUGGESTION_2: 1445 case IDC_SPELLCHECK_SUGGESTION_3: 1446 case IDC_SPELLCHECK_SUGGESTION_4: 1447 source_tab_contents_->render_view_host()->Replace( 1448 params_.dictionary_suggestions[id - IDC_SPELLCHECK_SUGGESTION_0]); 1449 break; 1450 1451 case IDC_CHECK_SPELLING_OF_THIS_FIELD: 1452 source_tab_contents_->render_view_host()->ToggleSpellCheck(); 1453 break; 1454 case IDC_SPELLCHECK_ADD_TO_DICTIONARY: { 1455 SpellCheckHost* spellcheck_host = profile_->GetSpellCheckHost(); 1456 if (!spellcheck_host) { 1457 NOTREACHED(); 1458 break; 1459 } 1460 spellcheck_host->AddWord(UTF16ToUTF8(params_.misspelled_word)); 1461 SpellCheckerPlatform::AddWord(params_.misspelled_word); 1462 break; 1463 } 1464 1465 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS: { 1466 std::string url = std::string(chrome::kChromeUISettingsURL) + 1467 chrome::kLanguageOptionsSubPage; 1468 OpenURL(GURL(url), NEW_FOREGROUND_TAB, PageTransition::LINK); 1469 break; 1470 } 1471 1472 case IDC_SPELLPANEL_TOGGLE: 1473 source_tab_contents_->render_view_host()->ToggleSpellPanel( 1474 SpellCheckerPlatform::SpellingPanelVisible()); 1475 break; 1476 1477 #if defined(OS_MACOSX) 1478 case IDC_WRITING_DIRECTION_DEFAULT: 1479 // WebKit's current behavior is for this menu item to always be disabled. 1480 NOTREACHED(); 1481 break; 1482 case IDC_WRITING_DIRECTION_RTL: 1483 case IDC_WRITING_DIRECTION_LTR: { 1484 WebKit::WebTextDirection dir = WebKit::WebTextDirectionLeftToRight; 1485 if (id == IDC_WRITING_DIRECTION_RTL) 1486 dir = WebKit::WebTextDirectionRightToLeft; 1487 source_tab_contents_->render_view_host()->UpdateTextDirection(dir); 1488 source_tab_contents_->render_view_host()->NotifyTextDirection(); 1489 break; 1490 } 1491 case IDC_CONTENT_CONTEXT_LOOK_UP_IN_DICTIONARY: 1492 LookUpInDictionary(); 1493 break; 1494 #endif // OS_MACOSX 1495 1496 default: 1497 NOTREACHED(); 1498 break; 1499 } 1500 } 1501 1502 void RenderViewContextMenu::MenuWillShow() { 1503 RenderWidgetHostView* view = source_tab_contents_->GetRenderWidgetHostView(); 1504 if (view) 1505 view->ShowingContextMenu(true); 1506 } 1507 1508 void RenderViewContextMenu::MenuClosed() { 1509 RenderWidgetHostView* view = source_tab_contents_->GetRenderWidgetHostView(); 1510 if (view) 1511 view->ShowingContextMenu(false); 1512 if (source_tab_contents_->render_view_host()) { 1513 source_tab_contents_->render_view_host()->ContextMenuClosed( 1514 params_.custom_context); 1515 } 1516 } 1517 1518 bool RenderViewContextMenu::IsDevCommandEnabled(int id) const { 1519 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 1520 if (command_line.HasSwitch(switches::kAlwaysEnableDevTools)) 1521 return true; 1522 1523 NavigationEntry *active_entry = 1524 source_tab_contents_->controller().GetActiveEntry(); 1525 if (!active_entry) 1526 return false; 1527 1528 // Don't inspect view source. 1529 if (active_entry->IsViewSourceMode()) 1530 return false; 1531 1532 // Don't inspect about:network, about:memory, etc. 1533 // However, we do want to inspect about:blank, which is often 1534 // used by ordinary web pages. 1535 if (active_entry->virtual_url().SchemeIs(chrome::kAboutScheme) && 1536 !LowerCaseEqualsASCII(active_entry->virtual_url().path(), "blank")) 1537 return false; 1538 1539 if (id == IDC_CONTENT_CONTEXT_INSPECTELEMENT) { 1540 // Don't enable the web inspector if JavaScript is disabled. 1541 if (!profile_->GetPrefs()->GetBoolean(prefs::kWebKitJavascriptEnabled) || 1542 command_line.HasSwitch(switches::kDisableJavaScript)) 1543 return false; 1544 // Don't enable the web inspector if the developer tools are disabled via 1545 // the preference dev-tools-disabled. 1546 if (profile_->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled)) 1547 return false; 1548 } 1549 1550 return true; 1551 } 1552 1553 string16 RenderViewContextMenu::PrintableSelectionText() { 1554 return l10n_util::TruncateString(params_.selection_text, 1555 kMaxSelectionTextLength); 1556 } 1557 1558 // Controller functions -------------------------------------------------------- 1559 1560 void RenderViewContextMenu::OpenURL( 1561 const GURL& url, 1562 WindowOpenDisposition disposition, 1563 PageTransition::Type transition) { 1564 source_tab_contents_->OpenURL(url, GURL(), disposition, transition); 1565 } 1566 1567 void RenderViewContextMenu::CopyImageAt(int x, int y) { 1568 source_tab_contents_->render_view_host()->CopyImageAt(x, y); 1569 } 1570 1571 void RenderViewContextMenu::Inspect(int x, int y) { 1572 UserMetrics::RecordAction(UserMetricsAction("DevTools_InspectElement"), 1573 profile_); 1574 DevToolsManager::GetInstance()->InspectElement( 1575 source_tab_contents_->render_view_host(), x, y); 1576 } 1577 1578 void RenderViewContextMenu::WriteURLToClipboard(const GURL& url) { 1579 chrome_browser_net::WriteURLToClipboard( 1580 url, 1581 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), 1582 g_browser_process->clipboard()); 1583 } 1584 1585 void RenderViewContextMenu::MediaPlayerActionAt( 1586 const gfx::Point& location, 1587 const WebMediaPlayerAction& action) { 1588 source_tab_contents_->render_view_host()->MediaPlayerActionAt( 1589 location, action); 1590 } 1591