| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <algorithm> | 5 #include <algorithm> |
| 6 #include <set> | 6 #include <set> |
| 7 | 7 |
| 8 #include "chrome/browser/tab_contents/render_view_context_menu.h" | 8 #include "chrome/browser/tab_contents/render_view_context_menu.h" |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 #include "chrome/browser/page_info_window.h" | 29 #include "chrome/browser/page_info_window.h" |
| 30 #include "chrome/browser/platform_util.h" | 30 #include "chrome/browser/platform_util.h" |
| 31 #include "chrome/browser/prefs/pref_member.h" | 31 #include "chrome/browser/prefs/pref_member.h" |
| 32 #include "chrome/browser/prefs/pref_service.h" | 32 #include "chrome/browser/prefs/pref_service.h" |
| 33 #include "chrome/browser/printing/print_preview_tab_controller.h" | 33 #include "chrome/browser/printing/print_preview_tab_controller.h" |
| 34 #include "chrome/browser/profiles/profile.h" | 34 #include "chrome/browser/profiles/profile.h" |
| 35 #include "chrome/browser/search_engines/template_url.h" | 35 #include "chrome/browser/search_engines/template_url.h" |
| 36 #include "chrome/browser/search_engines/template_url_model.h" | 36 #include "chrome/browser/search_engines/template_url_model.h" |
| 37 #include "chrome/browser/spellcheck_host.h" | 37 #include "chrome/browser/spellcheck_host.h" |
| 38 #include "chrome/browser/spellchecker_platform_engine.h" | 38 #include "chrome/browser/spellchecker_platform_engine.h" |
| 39 #include "chrome/browser/tab_contents/context_menu_utils.h" |
| 39 #include "chrome/browser/translate/translate_prefs.h" | 40 #include "chrome/browser/translate/translate_prefs.h" |
| 40 #include "chrome/browser/translate/translate_manager.h" | 41 #include "chrome/browser/translate/translate_manager.h" |
| 41 #include "chrome/common/chrome_constants.h" | 42 #include "chrome/common/chrome_constants.h" |
| 42 #include "chrome/common/chrome_switches.h" | 43 #include "chrome/common/chrome_switches.h" |
| 43 #include "chrome/common/content_restriction.h" | 44 #include "chrome/common/content_restriction.h" |
| 44 #include "chrome/common/pref_names.h" | 45 #include "chrome/common/pref_names.h" |
| 45 #include "chrome/common/print_messages.h" | 46 #include "chrome/common/print_messages.h" |
| 46 #include "chrome/common/url_constants.h" | 47 #include "chrome/common/url_constants.h" |
| 47 #include "content/browser/child_process_security_policy.h" | 48 #include "content/browser/child_process_security_policy.h" |
| 48 #include "content/browser/renderer_host/render_view_host.h" | 49 #include "content/browser/renderer_host/render_view_host.h" |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 RenderViewContextMenu::~RenderViewContextMenu() { | 190 RenderViewContextMenu::~RenderViewContextMenu() { |
| 190 } | 191 } |
| 191 | 192 |
| 192 // Menu construction functions ------------------------------------------------- | 193 // Menu construction functions ------------------------------------------------- |
| 193 | 194 |
| 194 void RenderViewContextMenu::Init() { | 195 void RenderViewContextMenu::Init() { |
| 195 InitMenu(); | 196 InitMenu(); |
| 196 PlatformInit(); | 197 PlatformInit(); |
| 197 } | 198 } |
| 198 | 199 |
| 199 static bool ExtensionContextMatch(const ContextMenuParams& params, | |
| 200 ExtensionMenuItem::ContextList contexts) { | |
| 201 bool has_link = !params.link_url.is_empty(); | |
| 202 bool has_selection = !params.selection_text.empty(); | |
| 203 | |
| 204 if (contexts.Contains(ExtensionMenuItem::ALL) || | |
| 205 (has_selection && contexts.Contains(ExtensionMenuItem::SELECTION)) || | |
| 206 (has_link && contexts.Contains(ExtensionMenuItem::LINK)) || | |
| 207 (params.is_editable && contexts.Contains(ExtensionMenuItem::EDITABLE))) { | |
| 208 return true; | |
| 209 } | |
| 210 | |
| 211 switch (params.media_type) { | |
| 212 case WebContextMenuData::MediaTypeImage: | |
| 213 return contexts.Contains(ExtensionMenuItem::IMAGE); | |
| 214 | |
| 215 case WebContextMenuData::MediaTypeVideo: | |
| 216 return contexts.Contains(ExtensionMenuItem::VIDEO); | |
| 217 | |
| 218 case WebContextMenuData::MediaTypeAudio: | |
| 219 return contexts.Contains(ExtensionMenuItem::AUDIO); | |
| 220 | |
| 221 default: | |
| 222 break; | |
| 223 } | |
| 224 | |
| 225 // PAGE is the least specific context, so we only examine that if none of the | |
| 226 // other contexts apply. | |
| 227 if (!has_link && !has_selection && !params.is_editable && | |
| 228 params.media_type == WebContextMenuData::MediaTypeNone && | |
| 229 contexts.Contains(ExtensionMenuItem::PAGE)) | |
| 230 return true; | |
| 231 | |
| 232 return false; | |
| 233 } | |
| 234 | |
| 235 static bool ExtensionPatternMatch(const ExtensionExtent& patterns, | |
| 236 const GURL& url) { | |
| 237 // No patterns means no restriction, so that implicitly matches. | |
| 238 if (patterns.is_empty()) | |
| 239 return true; | |
| 240 return patterns.ContainsURL(url); | |
| 241 } | |
| 242 | |
| 243 static const GURL& GetDocumentURL(const ContextMenuParams& params) { | |
| 244 return params.frame_url.is_empty() ? params.page_url : params.frame_url; | |
| 245 } | |
| 246 | |
| 247 // Given a list of items, returns the ones that match given the contents | |
| 248 // of |params| and the profile. | |
| 249 static ExtensionMenuItem::List GetRelevantExtensionItems( | |
| 250 const ExtensionMenuItem::List& items, | |
| 251 const ContextMenuParams& params, | |
| 252 Profile* profile, | |
| 253 bool can_cross_incognito) { | |
| 254 ExtensionMenuItem::List result; | |
| 255 for (ExtensionMenuItem::List::const_iterator i = items.begin(); | |
| 256 i != items.end(); ++i) { | |
| 257 const ExtensionMenuItem* item = *i; | |
| 258 | |
| 259 if (!ExtensionContextMatch(params, item->contexts())) | |
| 260 continue; | |
| 261 | |
| 262 const GURL& document_url = GetDocumentURL(params); | |
| 263 if (!ExtensionPatternMatch(item->document_url_patterns(), document_url)) | |
| 264 continue; | |
| 265 | |
| 266 const GURL& target_url = | |
| 267 params.src_url.is_empty() ? params.link_url : params.src_url; | |
| 268 if (!ExtensionPatternMatch(item->target_url_patterns(), target_url)) | |
| 269 continue; | |
| 270 | |
| 271 if (item->id().profile == profile || can_cross_incognito) | |
| 272 result.push_back(*i); | |
| 273 } | |
| 274 return result; | |
| 275 } | |
| 276 | |
| 277 void RenderViewContextMenu::AppendExtensionItems( | 200 void RenderViewContextMenu::AppendExtensionItems( |
| 278 const std::string& extension_id, int* index) { | 201 const std::string& extension_id, int* index) { |
| 279 ExtensionService* service = profile_->GetExtensionService(); | 202 ExtensionService* service = profile_->GetExtensionService(); |
| 280 ExtensionMenuManager* manager = service->menu_manager(); | 203 ExtensionMenuManager* manager = service->menu_manager(); |
| 281 const Extension* extension = service->GetExtensionById(extension_id, false); | 204 const Extension* extension = service->GetExtensionById(extension_id, false); |
| 282 DCHECK_GE(*index, 0); | 205 DCHECK_GE(*index, 0); |
| 283 int max_index = | 206 int max_index = |
| 284 IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; | 207 IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; |
| 285 if (!extension || *index >= max_index) | 208 if (!extension || *index >= max_index) |
| 286 return; | 209 return; |
| 287 | 210 |
| 288 // Find matching items. | 211 // Find matching items. |
| 289 const ExtensionMenuItem::List* all_items = manager->MenuItems(extension_id); | 212 const ExtensionMenuItem::List* all_items = manager->MenuItems(extension_id); |
| 290 if (!all_items || all_items->empty()) | 213 if (!all_items || all_items->empty()) |
| 291 return; | 214 return; |
| 292 bool can_cross_incognito = service->CanCrossIncognito(extension); | 215 bool can_cross_incognito = service->CanCrossIncognito(extension); |
| 293 ExtensionMenuItem::List items = | 216 ExtensionMenuItem::List items = |
| 294 GetRelevantExtensionItems(*all_items, params_, profile_, | 217 ContextMenuUtils::GetRelevantExtensionItems(*all_items, |
| 295 can_cross_incognito); | 218 params_, |
| 219 profile_, |
| 220 can_cross_incognito); |
| 296 if (items.empty()) | 221 if (items.empty()) |
| 297 return; | 222 return; |
| 298 | 223 |
| 299 // If this is the first extension-provided menu item, add a separator. | 224 // If this is the first extension-provided menu item, add a separator. |
| 300 if (*index == 0) | 225 if (*index == 0) |
| 301 menu_model_.AddSeparator(); | 226 menu_model_.AddSeparator(); |
| 302 | 227 |
| 303 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; | 228 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; |
| 304 | 229 |
| 305 // Extensions are only allowed one top-level slot (and it can't be a radio or | 230 // Extensions are only allowed one top-level slot (and it can't be a radio or |
| 306 // checkbox item because we are going to put the extension icon next to it). | 231 // checkbox item because we are going to put the extension icon next to it). |
| 307 // If they have more than that, we automatically push them into a submenu. | 232 // If they have more than that, we automatically push them into a submenu. |
| 308 string16 title; | 233 string16 title; |
| 309 ExtensionMenuItem::List submenu_items; | 234 ExtensionMenuItem::List submenu_items; |
| 310 if (items.size() > 1 || items[0]->type() != ExtensionMenuItem::NORMAL) { | 235 if (items.size() > 1 || items[0]->type() != ExtensionMenuItem::NORMAL) { |
| 311 title = UTF8ToUTF16(extension->name()); | 236 title = UTF8ToUTF16(extension->name()); |
| 312 submenu_items = items; | 237 submenu_items = items; |
| 313 } else { | 238 } else { |
| 314 ExtensionMenuItem* item = items[0]; | 239 ExtensionMenuItem* item = items[0]; |
| 315 extension_item_map_[menu_id] = item->id(); | 240 extension_item_map_[menu_id] = item->id(); |
| 316 title = item->TitleWithReplacement(PrintableSelectionText(), | 241 title = item->TitleWithReplacement(PrintableSelectionText(), |
| 317 kMaxExtensionItemTitleLength); | 242 kMaxExtensionItemTitleLength); |
| 318 submenu_items = GetRelevantExtensionItems(item->children(), params_, | 243 submenu_items = ContextMenuUtils::GetRelevantExtensionItems( |
| 319 profile_, can_cross_incognito); | 244 item->children(), params_, profile_, can_cross_incognito); |
| 320 } | 245 } |
| 321 | 246 |
| 322 // Now add our item(s) to the menu_model_. | 247 // Now add our item(s) to the menu_model_. |
| 323 if (submenu_items.empty()) { | 248 if (submenu_items.empty()) { |
| 324 menu_model_.AddItem(menu_id, title); | 249 menu_model_.AddItem(menu_id, title); |
| 325 } else { | 250 } else { |
| 326 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); | 251 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); |
| 327 extension_menu_models_.push_back(submenu); | 252 extension_menu_models_.push_back(submenu); |
| 328 menu_model_.AddSubMenu(menu_id, title, submenu); | 253 menu_model_.AddSubMenu(menu_id, title, submenu); |
| 329 RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, submenu, | 254 RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, submenu, |
| (...skipping 24 matching lines...) Expand all Loading... |
| 354 } | 279 } |
| 355 | 280 |
| 356 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; | 281 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; |
| 357 if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) | 282 if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) |
| 358 return; | 283 return; |
| 359 extension_item_map_[menu_id] = item->id(); | 284 extension_item_map_[menu_id] = item->id(); |
| 360 string16 title = item->TitleWithReplacement(selection_text, | 285 string16 title = item->TitleWithReplacement(selection_text, |
| 361 kMaxExtensionItemTitleLength); | 286 kMaxExtensionItemTitleLength); |
| 362 if (item->type() == ExtensionMenuItem::NORMAL) { | 287 if (item->type() == ExtensionMenuItem::NORMAL) { |
| 363 ExtensionMenuItem::List children = | 288 ExtensionMenuItem::List children = |
| 364 GetRelevantExtensionItems(item->children(), params_, | 289 ContextMenuUtils::GetRelevantExtensionItems( |
| 365 profile_, can_cross_incognito); | 290 item->children(), params_, profile_, can_cross_incognito); |
| 366 if (children.empty()) { | 291 if (children.empty()) { |
| 367 menu_model->AddItem(menu_id, title); | 292 menu_model->AddItem(menu_id, title); |
| 368 } else { | 293 } else { |
| 369 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); | 294 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); |
| 370 extension_menu_models_.push_back(submenu); | 295 extension_menu_models_.push_back(submenu); |
| 371 menu_model->AddSubMenu(menu_id, title, submenu); | 296 menu_model->AddSubMenu(menu_id, title, submenu); |
| 372 RecursivelyAppendExtensionItems(children, can_cross_incognito, | 297 RecursivelyAppendExtensionItems(children, can_cross_incognito, |
| 373 submenu, index); | 298 submenu, index); |
| 374 } | 299 } |
| 375 } else if (item->type() == ExtensionMenuItem::CHECKBOX) { | 300 } else if (item->type() == ExtensionMenuItem::CHECKBOX) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 407 | 332 |
| 408 menu_model_.SetIcon(index, icon); | 333 menu_model_.SetIcon(index, icon); |
| 409 } | 334 } |
| 410 | 335 |
| 411 void RenderViewContextMenu::AppendAllExtensionItems() { | 336 void RenderViewContextMenu::AppendAllExtensionItems() { |
| 412 extension_item_map_.clear(); | 337 extension_item_map_.clear(); |
| 413 ExtensionService* service = profile_->GetExtensionService(); | 338 ExtensionService* service = profile_->GetExtensionService(); |
| 414 if (!service) | 339 if (!service) |
| 415 return; // In unit-tests, we may not have an ExtensionService. | 340 return; // In unit-tests, we may not have an ExtensionService. |
| 416 ExtensionMenuManager* menu_manager = service->menu_manager(); | 341 ExtensionMenuManager* menu_manager = service->menu_manager(); |
| 417 const GURL& document_url = GetDocumentURL(params_); | 342 const GURL& document_url = ContextMenuUtils::GetDocumentURL(params_); |
| 418 if (!menu_manager->HasAllowedScheme(document_url)) | 343 if (!menu_manager->HasAllowedScheme(document_url)) |
| 419 return; | 344 return; |
| 420 | 345 |
| 421 // Get a list of extension id's that have context menu items, and sort it by | 346 // Get a list of extension id's that have context menu items, and sort it by |
| 422 // the extension's name. | 347 // the extension's name. |
| 423 std::set<std::string> ids = menu_manager->ExtensionIds(); | 348 std::set<std::string> ids = menu_manager->ExtensionIds(); |
| 424 std::vector<std::pair<std::string, std::string> > sorted_ids; | 349 std::vector<std::pair<std::string, std::string> > sorted_ids; |
| 425 for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) { | 350 for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) { |
| 426 const Extension* extension = service->GetExtensionById(*i, false); | 351 const Extension* extension = service->GetExtensionById(*i, false); |
| 427 if (extension) | 352 if (extension) |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 493 break; | 418 break; |
| 494 case WebContextMenuData::MediaTypeImage: | 419 case WebContextMenuData::MediaTypeImage: |
| 495 AppendImageItems(); | 420 AppendImageItems(); |
| 496 break; | 421 break; |
| 497 case WebContextMenuData::MediaTypeVideo: | 422 case WebContextMenuData::MediaTypeVideo: |
| 498 AppendVideoItems(); | 423 AppendVideoItems(); |
| 499 break; | 424 break; |
| 500 case WebContextMenuData::MediaTypeAudio: | 425 case WebContextMenuData::MediaTypeAudio: |
| 501 AppendAudioItems(); | 426 AppendAudioItems(); |
| 502 break; | 427 break; |
| 428 case WebContextMenuData::MediaTypeFile: |
| 429 AppendFileItems(); |
| 430 break; |
| 503 case WebContextMenuData::MediaTypePlugin: | 431 case WebContextMenuData::MediaTypePlugin: |
| 504 AppendPluginItems(); | 432 AppendPluginItems(); |
| 505 break; | 433 break; |
| 506 } | 434 } |
| 507 | 435 |
| 508 if (params_.is_editable) | 436 if (params_.is_editable) |
| 509 AppendEditableItems(); | 437 AppendEditableItems(); |
| 510 else if (has_selection) | 438 else if (has_selection) |
| 511 AppendCopyItem(); | 439 AppendCopyItem(); |
| 512 | 440 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 585 AppendMediaItems(); | 513 AppendMediaItems(); |
| 586 menu_model_.AddSeparator(); | 514 menu_model_.AddSeparator(); |
| 587 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, | 515 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, |
| 588 IDS_CONTENT_CONTEXT_SAVEVIDEOAS); | 516 IDS_CONTENT_CONTEXT_SAVEVIDEOAS); |
| 589 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, | 517 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, |
| 590 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION); | 518 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION); |
| 591 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, | 519 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, |
| 592 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB); | 520 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB); |
| 593 } | 521 } |
| 594 | 522 |
| 523 void RenderViewContextMenu::AppendFileItems() { |
| 524 // TODO(zelidrag): Add file context operations here (i.e. cut, copy, paste...) |
| 525 } |
| 526 |
| 595 void RenderViewContextMenu::AppendMediaItems() { | 527 void RenderViewContextMenu::AppendMediaItems() { |
| 596 int media_flags = params_.media_flags; | 528 int media_flags = params_.media_flags; |
| 597 | 529 |
| 598 menu_model_.AddItemWithStringId( | 530 menu_model_.AddItemWithStringId( |
| 599 IDC_CONTENT_CONTEXT_PLAYPAUSE, | 531 IDC_CONTENT_CONTEXT_PLAYPAUSE, |
| 600 media_flags & WebContextMenuData::MediaPaused ? | 532 media_flags & WebContextMenuData::MediaPaused ? |
| 601 IDS_CONTENT_CONTEXT_PLAY : | 533 IDS_CONTENT_CONTEXT_PLAY : |
| 602 IDS_CONTENT_CONTEXT_PAUSE); | 534 IDS_CONTENT_CONTEXT_PAUSE); |
| 603 | 535 |
| 604 menu_model_.AddItemWithStringId( | 536 menu_model_.AddItemWithStringId( |
| (...skipping 933 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1538 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), | 1470 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), |
| 1539 g_browser_process->clipboard()); | 1471 g_browser_process->clipboard()); |
| 1540 } | 1472 } |
| 1541 | 1473 |
| 1542 void RenderViewContextMenu::MediaPlayerActionAt( | 1474 void RenderViewContextMenu::MediaPlayerActionAt( |
| 1543 const gfx::Point& location, | 1475 const gfx::Point& location, |
| 1544 const WebMediaPlayerAction& action) { | 1476 const WebMediaPlayerAction& action) { |
| 1545 source_tab_contents_->render_view_host()->MediaPlayerActionAt( | 1477 source_tab_contents_->render_view_host()->MediaPlayerActionAt( |
| 1546 location, action); | 1478 location, action); |
| 1547 } | 1479 } |
| OLD | NEW |