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