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