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> | |
6 #include <set> | 5 #include <set> |
7 | 6 |
8 #include "chrome/browser/tab_contents/render_view_context_menu.h" | 7 #include "chrome/browser/tab_contents/render_view_context_menu.h" |
9 | 8 |
10 #include "base/command_line.h" | 9 #include "base/command_line.h" |
11 #include "base/logging.h" | 10 #include "base/logging.h" |
12 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
13 #include "base/stl_util-inl.h" | 12 #include "base/stl_util-inl.h" |
14 #include "base/string_util.h" | 13 #include "base/string_util.h" |
15 #include "base/time.h" | 14 #include "base/time.h" |
(...skipping 14 matching lines...) Expand all Loading... |
30 #include "chrome/browser/page_info_window.h" | 29 #include "chrome/browser/page_info_window.h" |
31 #include "chrome/browser/platform_util.h" | 30 #include "chrome/browser/platform_util.h" |
32 #include "chrome/browser/prefs/pref_member.h" | 31 #include "chrome/browser/prefs/pref_member.h" |
33 #include "chrome/browser/prefs/pref_service.h" | 32 #include "chrome/browser/prefs/pref_service.h" |
34 #include "chrome/browser/printing/print_preview_tab_controller.h" | 33 #include "chrome/browser/printing/print_preview_tab_controller.h" |
35 #include "chrome/browser/profiles/profile.h" | 34 #include "chrome/browser/profiles/profile.h" |
36 #include "chrome/browser/search_engines/template_url.h" | 35 #include "chrome/browser/search_engines/template_url.h" |
37 #include "chrome/browser/search_engines/template_url_model.h" | 36 #include "chrome/browser/search_engines/template_url_model.h" |
38 #include "chrome/browser/spellcheck_host.h" | 37 #include "chrome/browser/spellcheck_host.h" |
39 #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" |
40 #include "chrome/browser/translate/translate_manager.h" | 40 #include "chrome/browser/translate/translate_manager.h" |
41 #include "chrome/browser/translate/translate_prefs.h" | 41 #include "chrome/browser/translate/translate_prefs.h" |
42 #include "chrome/browser/ui/download/download_tab_helper.h" | 42 #include "chrome/browser/ui/download/download_tab_helper.h" |
43 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 43 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
44 #include "chrome/common/chrome_constants.h" | 44 #include "chrome/common/chrome_constants.h" |
45 #include "chrome/common/chrome_switches.h" | 45 #include "chrome/common/chrome_switches.h" |
46 #include "chrome/common/content_restriction.h" | 46 #include "chrome/common/content_restriction.h" |
47 #include "chrome/common/pref_names.h" | 47 #include "chrome/common/pref_names.h" |
48 #include "chrome/common/print_messages.h" | 48 #include "chrome/common/print_messages.h" |
49 #include "chrome/common/url_constants.h" | 49 #include "chrome/common/url_constants.h" |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
192 RenderViewContextMenu::~RenderViewContextMenu() { | 192 RenderViewContextMenu::~RenderViewContextMenu() { |
193 } | 193 } |
194 | 194 |
195 // Menu construction functions ------------------------------------------------- | 195 // Menu construction functions ------------------------------------------------- |
196 | 196 |
197 void RenderViewContextMenu::Init() { | 197 void RenderViewContextMenu::Init() { |
198 InitMenu(); | 198 InitMenu(); |
199 PlatformInit(); | 199 PlatformInit(); |
200 } | 200 } |
201 | 201 |
202 static bool ExtensionContextMatch(const ContextMenuParams& params, | |
203 ExtensionMenuItem::ContextList contexts) { | |
204 bool has_link = !params.link_url.is_empty(); | |
205 bool has_selection = !params.selection_text.empty(); | |
206 bool in_frame = !params.frame_url.is_empty(); | |
207 | |
208 if (contexts.Contains(ExtensionMenuItem::ALL) || | |
209 (has_selection && contexts.Contains(ExtensionMenuItem::SELECTION)) || | |
210 (has_link && contexts.Contains(ExtensionMenuItem::LINK)) || | |
211 (params.is_editable && contexts.Contains(ExtensionMenuItem::EDITABLE)) || | |
212 (in_frame && contexts.Contains(ExtensionMenuItem::FRAME))) { | |
213 return true; | |
214 } | |
215 | |
216 switch (params.media_type) { | |
217 case WebContextMenuData::MediaTypeImage: | |
218 return contexts.Contains(ExtensionMenuItem::IMAGE); | |
219 | |
220 case WebContextMenuData::MediaTypeVideo: | |
221 return contexts.Contains(ExtensionMenuItem::VIDEO); | |
222 | |
223 case WebContextMenuData::MediaTypeAudio: | |
224 return contexts.Contains(ExtensionMenuItem::AUDIO); | |
225 | |
226 default: | |
227 break; | |
228 } | |
229 | |
230 // PAGE is the least specific context, so we only examine that if none of the | |
231 // other contexts apply (except for FRAME, which is included in PAGE for | |
232 // backwards compatibility). | |
233 if (!has_link && !has_selection && !params.is_editable && | |
234 params.media_type == WebContextMenuData::MediaTypeNone && | |
235 contexts.Contains(ExtensionMenuItem::PAGE)) | |
236 return true; | |
237 | |
238 return false; | |
239 } | |
240 | |
241 static bool ExtensionPatternMatch(const ExtensionExtent& patterns, | |
242 const GURL& url) { | |
243 // No patterns means no restriction, so that implicitly matches. | |
244 if (patterns.is_empty()) | |
245 return true; | |
246 return patterns.ContainsURL(url); | |
247 } | |
248 | |
249 static const GURL& GetDocumentURL(const ContextMenuParams& params) { | |
250 return params.frame_url.is_empty() ? params.page_url : params.frame_url; | |
251 } | |
252 | |
253 // Given a list of items, returns the ones that match given the contents | |
254 // of |params| and the profile. | |
255 static ExtensionMenuItem::List GetRelevantExtensionItems( | |
256 const ExtensionMenuItem::List& items, | |
257 const ContextMenuParams& params, | |
258 Profile* profile, | |
259 bool can_cross_incognito) { | |
260 ExtensionMenuItem::List result; | |
261 for (ExtensionMenuItem::List::const_iterator i = items.begin(); | |
262 i != items.end(); ++i) { | |
263 const ExtensionMenuItem* item = *i; | |
264 | |
265 if (!ExtensionContextMatch(params, item->contexts())) | |
266 continue; | |
267 | |
268 const GURL& document_url = GetDocumentURL(params); | |
269 if (!ExtensionPatternMatch(item->document_url_patterns(), document_url)) | |
270 continue; | |
271 | |
272 const GURL& target_url = | |
273 params.src_url.is_empty() ? params.link_url : params.src_url; | |
274 if (!ExtensionPatternMatch(item->target_url_patterns(), target_url)) | |
275 continue; | |
276 | |
277 if (item->id().profile == profile || can_cross_incognito) | |
278 result.push_back(*i); | |
279 } | |
280 return result; | |
281 } | |
282 | |
283 void RenderViewContextMenu::AppendExtensionItems( | 202 void RenderViewContextMenu::AppendExtensionItems( |
284 const std::string& extension_id, int* index) { | 203 const std::string& extension_id, int* index) { |
285 ExtensionService* service = profile_->GetExtensionService(); | 204 ExtensionService* service = profile_->GetExtensionService(); |
286 ExtensionMenuManager* manager = service->menu_manager(); | 205 ExtensionMenuManager* manager = service->menu_manager(); |
287 const Extension* extension = service->GetExtensionById(extension_id, false); | 206 const Extension* extension = service->GetExtensionById(extension_id, false); |
288 DCHECK_GE(*index, 0); | 207 DCHECK_GE(*index, 0); |
289 int max_index = | 208 int max_index = |
290 IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; | 209 IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; |
291 if (!extension || *index >= max_index) | 210 if (!extension || *index >= max_index) |
292 return; | 211 return; |
293 | 212 |
294 // Find matching items. | 213 // Find matching items. |
295 const ExtensionMenuItem::List* all_items = manager->MenuItems(extension_id); | 214 const ExtensionMenuItem::List* all_items = manager->MenuItems(extension_id); |
296 if (!all_items || all_items->empty()) | 215 if (!all_items || all_items->empty()) |
297 return; | 216 return; |
298 bool can_cross_incognito = service->CanCrossIncognito(extension); | 217 bool can_cross_incognito = service->CanCrossIncognito(extension); |
299 ExtensionMenuItem::List items = | 218 ExtensionMenuItem::List items = |
300 GetRelevantExtensionItems(*all_items, params_, profile_, | 219 ContextMenuUtils::GetRelevantExtensionItems(*all_items, |
301 can_cross_incognito); | 220 params_, |
| 221 profile_, |
| 222 can_cross_incognito); |
302 if (items.empty()) | 223 if (items.empty()) |
303 return; | 224 return; |
304 | 225 |
305 // If this is the first extension-provided menu item, add a separator. | 226 // If this is the first extension-provided menu item, add a separator. |
306 if (*index == 0) | 227 if (*index == 0) |
307 menu_model_.AddSeparator(); | 228 menu_model_.AddSeparator(); |
308 | 229 |
309 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; | 230 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; |
310 | 231 |
311 // Extensions are only allowed one top-level slot (and it can't be a radio or | 232 // Extensions are only allowed one top-level slot (and it can't be a radio or |
312 // checkbox item because we are going to put the extension icon next to it). | 233 // checkbox item because we are going to put the extension icon next to it). |
313 // If they have more than that, we automatically push them into a submenu. | 234 // If they have more than that, we automatically push them into a submenu. |
314 string16 title; | 235 string16 title; |
315 ExtensionMenuItem::List submenu_items; | 236 ExtensionMenuItem::List submenu_items; |
316 if (items.size() > 1 || items[0]->type() != ExtensionMenuItem::NORMAL) { | 237 if (items.size() > 1 || items[0]->type() != ExtensionMenuItem::NORMAL) { |
317 title = UTF8ToUTF16(extension->name()); | 238 title = UTF8ToUTF16(extension->name()); |
318 submenu_items = items; | 239 submenu_items = items; |
319 } else { | 240 } else { |
320 ExtensionMenuItem* item = items[0]; | 241 ExtensionMenuItem* item = items[0]; |
321 extension_item_map_[menu_id] = item->id(); | 242 extension_item_map_[menu_id] = item->id(); |
322 title = item->TitleWithReplacement(PrintableSelectionText(), | 243 title = item->TitleWithReplacement(PrintableSelectionText(), |
323 kMaxExtensionItemTitleLength); | 244 kMaxExtensionItemTitleLength); |
324 submenu_items = GetRelevantExtensionItems(item->children(), params_, | 245 submenu_items = ContextMenuUtils::GetRelevantExtensionItems( |
325 profile_, can_cross_incognito); | 246 item->children(), params_, profile_, can_cross_incognito); |
326 } | 247 } |
327 | 248 |
328 // Now add our item(s) to the menu_model_. | 249 // Now add our item(s) to the menu_model_. |
329 if (submenu_items.empty()) { | 250 if (submenu_items.empty()) { |
330 menu_model_.AddItem(menu_id, title); | 251 menu_model_.AddItem(menu_id, title); |
331 } else { | 252 } else { |
332 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); | 253 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); |
333 extension_menu_models_.push_back(submenu); | 254 extension_menu_models_.push_back(submenu); |
334 menu_model_.AddSubMenu(menu_id, title, submenu); | 255 menu_model_.AddSubMenu(menu_id, title, submenu); |
335 RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, submenu, | 256 RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, submenu, |
(...skipping 24 matching lines...) Expand all Loading... |
360 } | 281 } |
361 | 282 |
362 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; | 283 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; |
363 if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) | 284 if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) |
364 return; | 285 return; |
365 extension_item_map_[menu_id] = item->id(); | 286 extension_item_map_[menu_id] = item->id(); |
366 string16 title = item->TitleWithReplacement(selection_text, | 287 string16 title = item->TitleWithReplacement(selection_text, |
367 kMaxExtensionItemTitleLength); | 288 kMaxExtensionItemTitleLength); |
368 if (item->type() == ExtensionMenuItem::NORMAL) { | 289 if (item->type() == ExtensionMenuItem::NORMAL) { |
369 ExtensionMenuItem::List children = | 290 ExtensionMenuItem::List children = |
370 GetRelevantExtensionItems(item->children(), params_, | 291 ContextMenuUtils::GetRelevantExtensionItems( |
371 profile_, can_cross_incognito); | 292 item->children(), params_, profile_, can_cross_incognito); |
372 if (children.empty()) { | 293 if (children.empty()) { |
373 menu_model->AddItem(menu_id, title); | 294 menu_model->AddItem(menu_id, title); |
374 } else { | 295 } else { |
375 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); | 296 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); |
376 extension_menu_models_.push_back(submenu); | 297 extension_menu_models_.push_back(submenu); |
377 menu_model->AddSubMenu(menu_id, title, submenu); | 298 menu_model->AddSubMenu(menu_id, title, submenu); |
378 RecursivelyAppendExtensionItems(children, can_cross_incognito, | 299 RecursivelyAppendExtensionItems(children, can_cross_incognito, |
379 submenu, index); | 300 submenu, index); |
380 } | 301 } |
381 } else if (item->type() == ExtensionMenuItem::CHECKBOX) { | 302 } else if (item->type() == ExtensionMenuItem::CHECKBOX) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
413 | 334 |
414 menu_model_.SetIcon(index, icon); | 335 menu_model_.SetIcon(index, icon); |
415 } | 336 } |
416 | 337 |
417 void RenderViewContextMenu::AppendAllExtensionItems() { | 338 void RenderViewContextMenu::AppendAllExtensionItems() { |
418 extension_item_map_.clear(); | 339 extension_item_map_.clear(); |
419 ExtensionService* service = profile_->GetExtensionService(); | 340 ExtensionService* service = profile_->GetExtensionService(); |
420 if (!service) | 341 if (!service) |
421 return; // In unit-tests, we may not have an ExtensionService. | 342 return; // In unit-tests, we may not have an ExtensionService. |
422 ExtensionMenuManager* menu_manager = service->menu_manager(); | 343 ExtensionMenuManager* menu_manager = service->menu_manager(); |
423 const GURL& document_url = GetDocumentURL(params_); | 344 const GURL& document_url = ContextMenuUtils::GetDocumentURL(params_); |
424 if (!menu_manager->HasAllowedScheme(document_url)) | 345 if (!menu_manager->HasAllowedScheme(document_url)) |
425 return; | 346 return; |
426 | 347 |
427 // Get a list of extension id's that have context menu items, and sort it by | |
428 // the extension's name. | |
429 std::set<std::string> ids = menu_manager->ExtensionIds(); | |
430 std::vector<std::pair<std::string, std::string> > sorted_ids; | 348 std::vector<std::pair<std::string, std::string> > sorted_ids; |
431 for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) { | 349 ContextMenuUtils::GetSortedContextMenuExtensionIds(service, menu_manager, |
432 const Extension* extension = service->GetExtensionById(*i, false); | 350 &sorted_ids); |
433 if (extension) | |
434 sorted_ids.push_back( | |
435 std::pair<std::string, std::string>(extension->name(), *i)); | |
436 } | |
437 // TODO(asargent) - See if this works properly for i18n names (bug 32363). | |
438 std::sort(sorted_ids.begin(), sorted_ids.end()); | |
439 | |
440 if (sorted_ids.empty()) | 351 if (sorted_ids.empty()) |
441 return; | 352 return; |
442 | 353 |
443 int index = 0; | 354 int index = 0; |
444 base::TimeTicks begin = base::TimeTicks::Now(); | 355 base::TimeTicks begin = base::TimeTicks::Now(); |
445 std::vector<std::pair<std::string, std::string> >::const_iterator i; | 356 std::vector<std::pair<std::string, std::string> >::const_iterator i; |
446 for (i = sorted_ids.begin(); | 357 for (i = sorted_ids.begin(); |
447 i != sorted_ids.end(); ++i) { | 358 i != sorted_ids.end(); ++i) { |
448 AppendExtensionItems(i->second, &index); | 359 AppendExtensionItems(i->second, &index); |
449 } | 360 } |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
502 break; | 413 break; |
503 case WebContextMenuData::MediaTypeVideo: | 414 case WebContextMenuData::MediaTypeVideo: |
504 AppendVideoItems(); | 415 AppendVideoItems(); |
505 break; | 416 break; |
506 case WebContextMenuData::MediaTypeAudio: | 417 case WebContextMenuData::MediaTypeAudio: |
507 AppendAudioItems(); | 418 AppendAudioItems(); |
508 break; | 419 break; |
509 case WebContextMenuData::MediaTypePlugin: | 420 case WebContextMenuData::MediaTypePlugin: |
510 AppendPluginItems(); | 421 AppendPluginItems(); |
511 break; | 422 break; |
512 #ifdef WEBCONTEXT_MEDIATYPEFILE_DEFINED | |
513 case WebContextMenuData::MediaTypeFile: | 423 case WebContextMenuData::MediaTypeFile: |
| 424 AppendFileItems(); |
514 break; | 425 break; |
515 #endif | |
516 } | 426 } |
517 | 427 |
518 if (params_.is_editable) | 428 if (params_.is_editable) |
519 AppendEditableItems(); | 429 AppendEditableItems(); |
520 else if (has_selection) | 430 else if (has_selection) |
521 AppendCopyItem(); | 431 AppendCopyItem(); |
522 | 432 |
523 if (has_selection) | 433 if (has_selection) |
524 AppendSearchProvider(); | 434 AppendSearchProvider(); |
525 | 435 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
595 AppendMediaItems(); | 505 AppendMediaItems(); |
596 menu_model_.AddSeparator(); | 506 menu_model_.AddSeparator(); |
597 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, | 507 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, |
598 IDS_CONTENT_CONTEXT_SAVEVIDEOAS); | 508 IDS_CONTENT_CONTEXT_SAVEVIDEOAS); |
599 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, | 509 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, |
600 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION); | 510 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION); |
601 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, | 511 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, |
602 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB); | 512 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB); |
603 } | 513 } |
604 | 514 |
| 515 void RenderViewContextMenu::AppendFileItems() { |
| 516 // TODO(zelidrag): Add file context operations here (i.e. cut, copy, paste...) |
| 517 } |
| 518 |
605 void RenderViewContextMenu::AppendMediaItems() { | 519 void RenderViewContextMenu::AppendMediaItems() { |
606 int media_flags = params_.media_flags; | 520 int media_flags = params_.media_flags; |
607 | 521 |
608 menu_model_.AddItemWithStringId( | 522 menu_model_.AddItemWithStringId( |
609 IDC_CONTENT_CONTEXT_PLAYPAUSE, | 523 IDC_CONTENT_CONTEXT_PLAYPAUSE, |
610 media_flags & WebContextMenuData::MediaPaused ? | 524 media_flags & WebContextMenuData::MediaPaused ? |
611 IDS_CONTENT_CONTEXT_PLAY : | 525 IDS_CONTENT_CONTEXT_PLAY : |
612 IDS_CONTENT_CONTEXT_PAUSE); | 526 IDS_CONTENT_CONTEXT_PAUSE); |
613 | 527 |
614 menu_model_.AddItemWithStringId( | 528 menu_model_.AddItemWithStringId( |
(...skipping 937 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1552 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), | 1466 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), |
1553 g_browser_process->clipboard()); | 1467 g_browser_process->clipboard()); |
1554 } | 1468 } |
1555 | 1469 |
1556 void RenderViewContextMenu::MediaPlayerActionAt( | 1470 void RenderViewContextMenu::MediaPlayerActionAt( |
1557 const gfx::Point& location, | 1471 const gfx::Point& location, |
1558 const WebMediaPlayerAction& action) { | 1472 const WebMediaPlayerAction& action) { |
1559 source_tab_contents_->render_view_host()->MediaPlayerActionAt( | 1473 source_tab_contents_->render_view_host()->MediaPlayerActionAt( |
1560 location, action); | 1474 location, action); |
1561 } | 1475 } |
OLD | NEW |