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