| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 "chrome/browser/tab_contents/render_view_context_menu.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <set> | |
| 9 #include <utility> | |
| 10 | |
| 11 #include "apps/app_load_service.h" | |
| 12 #include "base/command_line.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/metrics/histogram.h" | |
| 15 #include "base/prefs/pref_member.h" | |
| 16 #include "base/prefs/pref_service.h" | |
| 17 #include "base/stl_util.h" | |
| 18 #include "base/strings/string_util.h" | |
| 19 #include "base/strings/stringprintf.h" | |
| 20 #include "base/strings/utf_string_conversions.h" | |
| 21 #include "base/time/time.h" | |
| 22 #include "chrome/app/chrome_command_ids.h" | |
| 23 #include "chrome/browser/app_mode/app_mode_utils.h" | |
| 24 #include "chrome/browser/autocomplete/autocomplete_classifier.h" | |
| 25 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" | |
| 26 #include "chrome/browser/autocomplete/autocomplete_input.h" | |
| 27 #include "chrome/browser/autocomplete/autocomplete_match.h" | |
| 28 #include "chrome/browser/browser_process.h" | |
| 29 #include "chrome/browser/chrome_notification_types.h" | |
| 30 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" | |
| 31 #include "chrome/browser/devtools/devtools_window.h" | |
| 32 #include "chrome/browser/download/download_service.h" | |
| 33 #include "chrome/browser/download/download_service_factory.h" | |
| 34 #include "chrome/browser/download/download_stats.h" | |
| 35 #include "chrome/browser/extensions/devtools_util.h" | |
| 36 #include "chrome/browser/extensions/extension_host.h" | |
| 37 #include "chrome/browser/extensions/extension_service.h" | |
| 38 #include "chrome/browser/google/google_util.h" | |
| 39 #include "chrome/browser/plugins/chrome_plugin_service_filter.h" | |
| 40 #include "chrome/browser/prefs/incognito_mode_prefs.h" | |
| 41 #include "chrome/browser/profiles/profile.h" | |
| 42 #include "chrome/browser/profiles/profile_io_data.h" | |
| 43 #include "chrome/browser/search/search.h" | |
| 44 #include "chrome/browser/search_engines/search_terms_data.h" | |
| 45 #include "chrome/browser/search_engines/template_url.h" | |
| 46 #include "chrome/browser/search_engines/template_url_service.h" | |
| 47 #include "chrome/browser/search_engines/template_url_service_factory.h" | |
| 48 #include "chrome/browser/spellchecker/spellcheck_host_metrics.h" | |
| 49 #include "chrome/browser/spellchecker/spellcheck_service.h" | |
| 50 #include "chrome/browser/tab_contents/retargeting_details.h" | |
| 51 #include "chrome/browser/tab_contents/spellchecker_submenu_observer.h" | |
| 52 #include "chrome/browser/tab_contents/spelling_menu_observer.h" | |
| 53 #include "chrome/browser/translate/translate_manager.h" | |
| 54 #include "chrome/browser/translate/translate_tab_helper.h" | |
| 55 #include "chrome/browser/ui/browser.h" | |
| 56 #include "chrome/browser/ui/browser_commands.h" | |
| 57 #include "chrome/browser/ui/browser_finder.h" | |
| 58 #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h" | |
| 59 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" | |
| 60 #include "chrome/common/chrome_constants.h" | |
| 61 #include "chrome/common/chrome_switches.h" | |
| 62 #include "chrome/common/content_restriction.h" | |
| 63 #include "chrome/common/net/url_util.h" | |
| 64 #include "chrome/common/pref_names.h" | |
| 65 #include "chrome/common/render_messages.h" | |
| 66 #include "chrome/common/spellcheck_messages.h" | |
| 67 #include "chrome/common/url_constants.h" | |
| 68 #include "components/translate/core/browser/translate_download_manager.h" | |
| 69 #include "components/translate/core/browser/translate_prefs.h" | |
| 70 #include "content/public/browser/child_process_security_policy.h" | |
| 71 #include "content/public/browser/download_manager.h" | |
| 72 #include "content/public/browser/download_save_info.h" | |
| 73 #include "content/public/browser/download_url_parameters.h" | |
| 74 #include "content/public/browser/navigation_details.h" | |
| 75 #include "content/public/browser/navigation_entry.h" | |
| 76 #include "content/public/browser/notification_service.h" | |
| 77 #include "content/public/browser/render_frame_host.h" | |
| 78 #include "content/public/browser/render_process_host.h" | |
| 79 #include "content/public/browser/render_view_host.h" | |
| 80 #include "content/public/browser/render_widget_host_view.h" | |
| 81 #include "content/public/browser/user_metrics.h" | |
| 82 #include "content/public/browser/web_contents.h" | |
| 83 #include "content/public/common/menu_item.h" | |
| 84 #include "content/public/common/ssl_status.h" | |
| 85 #include "content/public/common/url_utils.h" | |
| 86 #include "extensions/browser/extension_system.h" | |
| 87 #include "extensions/browser/view_type_utils.h" | |
| 88 #include "extensions/common/extension.h" | |
| 89 #include "grit/generated_resources.h" | |
| 90 #include "net/base/escape.h" | |
| 91 #include "third_party/WebKit/public/web/WebContextMenuData.h" | |
| 92 #include "third_party/WebKit/public/web/WebMediaPlayerAction.h" | |
| 93 #include "third_party/WebKit/public/web/WebPluginAction.h" | |
| 94 #include "ui/base/clipboard/clipboard.h" | |
| 95 #include "ui/base/l10n/l10n_util.h" | |
| 96 #include "ui/gfx/favicon_size.h" | |
| 97 #include "ui/gfx/point.h" | |
| 98 #include "ui/gfx/size.h" | |
| 99 #include "ui/gfx/text_elider.h" | |
| 100 | |
| 101 #if defined(ENABLE_PRINTING) | |
| 102 #include "chrome/common/print_messages.h" | |
| 103 | |
| 104 #if defined(ENABLE_FULL_PRINTING) | |
| 105 #include "chrome/browser/printing/print_preview_context_menu_observer.h" | |
| 106 #include "chrome/browser/printing/print_preview_dialog_controller.h" | |
| 107 #include "chrome/browser/printing/print_view_manager.h" | |
| 108 #else | |
| 109 #include "chrome/browser/printing/print_view_manager_basic.h" | |
| 110 #endif // defined(ENABLE_FULL_PRINTING) | |
| 111 #endif // defined(ENABLE_PRINTING) | |
| 112 | |
| 113 using base::UserMetricsAction; | |
| 114 using blink::WebContextMenuData; | |
| 115 using blink::WebMediaPlayerAction; | |
| 116 using blink::WebPluginAction; | |
| 117 using blink::WebString; | |
| 118 using blink::WebURL; | |
| 119 using content::BrowserContext; | |
| 120 using content::ChildProcessSecurityPolicy; | |
| 121 using content::DownloadManager; | |
| 122 using content::DownloadUrlParameters; | |
| 123 using content::NavigationController; | |
| 124 using content::NavigationEntry; | |
| 125 using content::OpenURLParams; | |
| 126 using content::RenderFrameHost; | |
| 127 using content::RenderViewHost; | |
| 128 using content::SSLStatus; | |
| 129 using content::WebContents; | |
| 130 using extensions::Extension; | |
| 131 using extensions::MenuItem; | |
| 132 using extensions::MenuManager; | |
| 133 | |
| 134 namespace { | |
| 135 | |
| 136 const int kImageSearchThumbnailMinSize = 300 * 300; | |
| 137 const int kImageSearchThumbnailMaxWidth = 600; | |
| 138 const int kImageSearchThumbnailMaxHeight = 600; | |
| 139 | |
| 140 // Maps UMA enumeration to IDC. IDC could be changed so we can't use | |
| 141 // just them and |UMA_HISTOGRAM_CUSTOM_ENUMERATION|. | |
| 142 // Never change mapping or reuse |enum_id|. Always push back new items. | |
| 143 // Items that is not used any more by |RenderViewContextMenu.ExecuteCommand| | |
| 144 // could be deleted, but don't change the rest of |kUmaEnumToControlId|. | |
| 145 const struct UmaEnumCommandIdPair { | |
| 146 int enum_id; | |
| 147 int control_id; | |
| 148 } kUmaEnumToControlId[] = { | |
| 149 { 0, IDC_CONTENT_CONTEXT_CUSTOM_FIRST }, | |
| 150 { 1, IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST }, | |
| 151 { 2, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST }, | |
| 152 { 3, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB }, | |
| 153 { 4, IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW }, | |
| 154 { 5, IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD }, | |
| 155 { 6, IDC_CONTENT_CONTEXT_SAVELINKAS }, | |
| 156 { 7, IDC_CONTENT_CONTEXT_SAVEAVAS }, | |
| 157 { 8, IDC_CONTENT_CONTEXT_SAVEIMAGEAS }, | |
| 158 { 9, IDC_CONTENT_CONTEXT_COPYLINKLOCATION }, | |
| 159 { 10, IDC_CONTENT_CONTEXT_COPYIMAGELOCATION }, | |
| 160 { 11, IDC_CONTENT_CONTEXT_COPYAVLOCATION }, | |
| 161 { 12, IDC_CONTENT_CONTEXT_COPYIMAGE }, | |
| 162 { 13, IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB }, | |
| 163 { 14, IDC_CONTENT_CONTEXT_OPENAVNEWTAB }, | |
| 164 { 15, IDC_CONTENT_CONTEXT_PLAYPAUSE }, | |
| 165 { 16, IDC_CONTENT_CONTEXT_MUTE }, | |
| 166 { 17, IDC_CONTENT_CONTEXT_LOOP }, | |
| 167 { 18, IDC_CONTENT_CONTEXT_CONTROLS }, | |
| 168 { 19, IDC_CONTENT_CONTEXT_ROTATECW }, | |
| 169 { 20, IDC_CONTENT_CONTEXT_ROTATECCW }, | |
| 170 { 21, IDC_BACK }, | |
| 171 { 22, IDC_FORWARD }, | |
| 172 { 23, IDC_SAVE_PAGE }, | |
| 173 { 24, IDC_RELOAD }, | |
| 174 { 25, IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP }, | |
| 175 { 26, IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP }, | |
| 176 { 27, IDC_PRINT }, | |
| 177 { 28, IDC_VIEW_SOURCE }, | |
| 178 { 29, IDC_CONTENT_CONTEXT_INSPECTELEMENT }, | |
| 179 { 30, IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE }, | |
| 180 { 31, IDC_CONTENT_CONTEXT_VIEWPAGEINFO }, | |
| 181 { 32, IDC_CONTENT_CONTEXT_TRANSLATE }, | |
| 182 { 33, IDC_CONTENT_CONTEXT_RELOADFRAME }, | |
| 183 { 34, IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE }, | |
| 184 { 35, IDC_CONTENT_CONTEXT_VIEWFRAMEINFO }, | |
| 185 { 36, IDC_CONTENT_CONTEXT_UNDO }, | |
| 186 { 37, IDC_CONTENT_CONTEXT_REDO }, | |
| 187 { 38, IDC_CONTENT_CONTEXT_CUT }, | |
| 188 { 39, IDC_CONTENT_CONTEXT_COPY }, | |
| 189 { 40, IDC_CONTENT_CONTEXT_PASTE }, | |
| 190 { 41, IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE }, | |
| 191 { 42, IDC_CONTENT_CONTEXT_DELETE }, | |
| 192 { 43, IDC_CONTENT_CONTEXT_SELECTALL }, | |
| 193 { 44, IDC_CONTENT_CONTEXT_SEARCHWEBFOR }, | |
| 194 { 45, IDC_CONTENT_CONTEXT_GOTOURL }, | |
| 195 { 46, IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS }, | |
| 196 { 47, IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS }, | |
| 197 { 48, IDC_CONTENT_CONTEXT_ADDSEARCHENGINE }, | |
| 198 { 49, IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES }, | |
| 199 { 50, IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT }, | |
| 200 { 51, IDC_SPEECH_INPUT_MENU }, | |
| 201 { 52, IDC_CONTENT_CONTEXT_OPENLINKWITH }, | |
| 202 { 53, IDC_CHECK_SPELLING_WHILE_TYPING }, | |
| 203 { 54, IDC_SPELLCHECK_MENU }, | |
| 204 { 55, IDC_CONTENT_CONTEXT_SPELLING_TOGGLE }, | |
| 205 { 56, IDC_SPELLCHECK_LANGUAGES_FIRST }, | |
| 206 { 57, IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE }, | |
| 207 // Add new items here and use |enum_id| from the next line. | |
| 208 { 58, 0 }, // Must be the last. Increment |enum_id| when new IDC was added. | |
| 209 }; | |
| 210 | |
| 211 // Collapses large ranges of ids before looking for UMA enum. | |
| 212 int CollapleCommandsForUMA(int id) { | |
| 213 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && | |
| 214 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { | |
| 215 return IDC_CONTENT_CONTEXT_CUSTOM_FIRST; | |
| 216 } | |
| 217 | |
| 218 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && | |
| 219 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { | |
| 220 return IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; | |
| 221 } | |
| 222 | |
| 223 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST && | |
| 224 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) { | |
| 225 return IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST; | |
| 226 } | |
| 227 | |
| 228 if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST && | |
| 229 id <= IDC_SPELLCHECK_LANGUAGES_LAST) { | |
| 230 return IDC_SPELLCHECK_LANGUAGES_FIRST; | |
| 231 } | |
| 232 | |
| 233 return id; | |
| 234 } | |
| 235 | |
| 236 // Returns UMA enum value for command specified by |id| or -1 if not found. | |
| 237 int FindUMAEnumValueForCommand(int id) { | |
| 238 id = CollapleCommandsForUMA(id); | |
| 239 const size_t kMappingSize = arraysize(kUmaEnumToControlId); | |
| 240 for (size_t i = 0; i < kMappingSize; ++i) { | |
| 241 if (kUmaEnumToControlId[i].control_id == id) { | |
| 242 return kUmaEnumToControlId[i].enum_id; | |
| 243 } | |
| 244 } | |
| 245 return -1; | |
| 246 } | |
| 247 | |
| 248 // Increments histogram value for used items specified by |id|. | |
| 249 void RecordUsedItem(int id) { | |
| 250 int enum_id = FindUMAEnumValueForCommand(id); | |
| 251 if (enum_id != -1) { | |
| 252 const size_t kMappingSize = arraysize(kUmaEnumToControlId); | |
| 253 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Used", enum_id, | |
| 254 kUmaEnumToControlId[kMappingSize - 1].enum_id); | |
| 255 } else { | |
| 256 NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id; | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 // Increments histogram value for visible context menu item specified by |id|. | |
| 261 void RecordShownItem(int id) { | |
| 262 int enum_id = FindUMAEnumValueForCommand(id); | |
| 263 if (enum_id != -1) { | |
| 264 const size_t kMappingSize = arraysize(kUmaEnumToControlId); | |
| 265 UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Shown", enum_id, | |
| 266 kUmaEnumToControlId[kMappingSize - 1].enum_id); | |
| 267 } else { | |
| 268 // Just warning here. It's harder to maintain list of all possibly | |
| 269 // visible items than executable items. | |
| 270 DLOG(ERROR) << "Update kUmaEnumToControlId. Unhanded IDC: " << id; | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 // Usually a new tab is expected where this function is used, | |
| 275 // however users should be able to open a tab in background | |
| 276 // or in a new window. | |
| 277 WindowOpenDisposition ForceNewTabDispositionFromEventFlags( | |
| 278 int event_flags) { | |
| 279 WindowOpenDisposition disposition = | |
| 280 ui::DispositionFromEventFlags(event_flags); | |
| 281 return disposition == CURRENT_TAB ? NEW_FOREGROUND_TAB : disposition; | |
| 282 } | |
| 283 | |
| 284 bool IsCustomItemEnabled(const std::vector<content::MenuItem>& items, int id) { | |
| 285 DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && | |
| 286 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST); | |
| 287 for (size_t i = 0; i < items.size(); ++i) { | |
| 288 int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action; | |
| 289 if (action_id == id) | |
| 290 return items[i].enabled; | |
| 291 if (items[i].type == content::MenuItem::SUBMENU) { | |
| 292 if (IsCustomItemEnabled(items[i].submenu, id)) | |
| 293 return true; | |
| 294 } | |
| 295 } | |
| 296 return false; | |
| 297 } | |
| 298 | |
| 299 bool IsCustomItemChecked(const std::vector<content::MenuItem>& items, int id) { | |
| 300 DCHECK(id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && | |
| 301 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST); | |
| 302 for (size_t i = 0; i < items.size(); ++i) { | |
| 303 int action_id = IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action; | |
| 304 if (action_id == id) | |
| 305 return items[i].checked; | |
| 306 if (items[i].type == content::MenuItem::SUBMENU) { | |
| 307 if (IsCustomItemChecked(items[i].submenu, id)) | |
| 308 return true; | |
| 309 } | |
| 310 } | |
| 311 return false; | |
| 312 } | |
| 313 | |
| 314 const size_t kMaxCustomMenuDepth = 5; | |
| 315 const size_t kMaxCustomMenuTotalItems = 1000; | |
| 316 | |
| 317 void AddCustomItemsToMenu(const std::vector<content::MenuItem>& items, | |
| 318 size_t depth, | |
| 319 size_t* total_items, | |
| 320 ui::SimpleMenuModel::Delegate* delegate, | |
| 321 ui::SimpleMenuModel* menu_model) { | |
| 322 if (depth > kMaxCustomMenuDepth) { | |
| 323 LOG(ERROR) << "Custom menu too deeply nested."; | |
| 324 return; | |
| 325 } | |
| 326 for (size_t i = 0; i < items.size(); ++i) { | |
| 327 if (IDC_CONTENT_CONTEXT_CUSTOM_FIRST + items[i].action >= | |
| 328 IDC_CONTENT_CONTEXT_CUSTOM_LAST) { | |
| 329 LOG(ERROR) << "Custom menu action value too big."; | |
| 330 return; | |
| 331 } | |
| 332 if (*total_items >= kMaxCustomMenuTotalItems) { | |
| 333 LOG(ERROR) << "Custom menu too large (too many items)."; | |
| 334 return; | |
| 335 } | |
| 336 (*total_items)++; | |
| 337 switch (items[i].type) { | |
| 338 case content::MenuItem::OPTION: | |
| 339 menu_model->AddItem( | |
| 340 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST, | |
| 341 items[i].label); | |
| 342 break; | |
| 343 case content::MenuItem::CHECKABLE_OPTION: | |
| 344 menu_model->AddCheckItem( | |
| 345 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST, | |
| 346 items[i].label); | |
| 347 break; | |
| 348 case content::MenuItem::GROUP: | |
| 349 // TODO(viettrungluu): I don't know what this is supposed to do. | |
| 350 NOTREACHED(); | |
| 351 break; | |
| 352 case content::MenuItem::SEPARATOR: | |
| 353 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); | |
| 354 break; | |
| 355 case content::MenuItem::SUBMENU: { | |
| 356 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate); | |
| 357 AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate, | |
| 358 submenu); | |
| 359 menu_model->AddSubMenu( | |
| 360 items[i].action + IDC_CONTENT_CONTEXT_CUSTOM_FIRST, | |
| 361 items[i].label, | |
| 362 submenu); | |
| 363 break; | |
| 364 } | |
| 365 default: | |
| 366 NOTREACHED(); | |
| 367 break; | |
| 368 } | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 void DevToolsInspectElementAt(RenderViewHost* rvh, int x, int y) { | |
| 373 DevToolsWindow::InspectElement(rvh, x, y); | |
| 374 } | |
| 375 | |
| 376 // Helper function to escape "&" as "&&". | |
| 377 void EscapeAmpersands(base::string16* text) { | |
| 378 const base::char16 ampersand[] = {'&', 0}; | |
| 379 base::ReplaceChars(*text, ampersand, base::ASCIIToUTF16("&&"), text); | |
| 380 } | |
| 381 | |
| 382 } // namespace | |
| 383 | |
| 384 // static | |
| 385 const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50; | |
| 386 | |
| 387 // static | |
| 388 bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) { | |
| 389 return url.SchemeIs(content::kChromeDevToolsScheme); | |
| 390 } | |
| 391 | |
| 392 // static | |
| 393 bool RenderViewContextMenu::IsInternalResourcesURL(const GURL& url) { | |
| 394 if (!url.SchemeIs(content::kChromeUIScheme)) | |
| 395 return false; | |
| 396 return url.host() == chrome::kChromeUISyncResourcesHost; | |
| 397 } | |
| 398 | |
| 399 static const int kSpellcheckRadioGroup = 1; | |
| 400 | |
| 401 RenderViewContextMenu::RenderViewContextMenu( | |
| 402 content::RenderFrameHost* render_frame_host, | |
| 403 const content::ContextMenuParams& params) | |
| 404 : params_(params), | |
| 405 source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), | |
| 406 render_process_id_(render_frame_host->GetProcess()->GetID()), | |
| 407 render_frame_id_(render_frame_host->GetRoutingID()), | |
| 408 profile_(Profile::FromBrowserContext( | |
| 409 source_web_contents_->GetBrowserContext())), | |
| 410 menu_model_(this), | |
| 411 extension_items_(profile_, this, &menu_model_, | |
| 412 base::Bind(MenuItemMatchesParams, params_)), | |
| 413 speech_input_submenu_model_(this), | |
| 414 protocol_handler_submenu_model_(this), | |
| 415 protocol_handler_registry_( | |
| 416 ProtocolHandlerRegistryFactory::GetForProfile(profile_)), | |
| 417 command_executed_(false), | |
| 418 is_guest_(false) { | |
| 419 RenderViewHost* rvh = source_web_contents_->GetRenderViewHost(); | |
| 420 if (rvh && rvh->GetProcess()->IsGuest()) | |
| 421 is_guest_ = true; | |
| 422 } | |
| 423 | |
| 424 RenderViewContextMenu::~RenderViewContextMenu() { | |
| 425 } | |
| 426 | |
| 427 // Menu construction functions ------------------------------------------------- | |
| 428 | |
| 429 void RenderViewContextMenu::Init() { | |
| 430 InitMenu(); | |
| 431 PlatformInit(); | |
| 432 } | |
| 433 | |
| 434 void RenderViewContextMenu::Cancel() { | |
| 435 PlatformCancel(); | |
| 436 } | |
| 437 | |
| 438 static bool ExtensionPatternMatch(const extensions::URLPatternSet& patterns, | |
| 439 const GURL& url) { | |
| 440 // No patterns means no restriction, so that implicitly matches. | |
| 441 if (patterns.is_empty()) | |
| 442 return true; | |
| 443 return patterns.MatchesURL(url); | |
| 444 } | |
| 445 | |
| 446 // static | |
| 447 bool RenderViewContextMenu::ExtensionContextAndPatternMatch( | |
| 448 const content::ContextMenuParams& params, | |
| 449 MenuItem::ContextList contexts, | |
| 450 const extensions::URLPatternSet& target_url_patterns) { | |
| 451 const bool has_link = !params.link_url.is_empty(); | |
| 452 const bool has_selection = !params.selection_text.empty(); | |
| 453 const bool in_frame = !params.frame_url.is_empty(); | |
| 454 | |
| 455 if (contexts.Contains(MenuItem::ALL) || | |
| 456 (has_selection && contexts.Contains(MenuItem::SELECTION)) || | |
| 457 (params.is_editable && contexts.Contains(MenuItem::EDITABLE)) || | |
| 458 (in_frame && contexts.Contains(MenuItem::FRAME))) | |
| 459 return true; | |
| 460 | |
| 461 if (has_link && contexts.Contains(MenuItem::LINK) && | |
| 462 ExtensionPatternMatch(target_url_patterns, params.link_url)) | |
| 463 return true; | |
| 464 | |
| 465 switch (params.media_type) { | |
| 466 case WebContextMenuData::MediaTypeImage: | |
| 467 if (contexts.Contains(MenuItem::IMAGE) && | |
| 468 ExtensionPatternMatch(target_url_patterns, params.src_url)) | |
| 469 return true; | |
| 470 break; | |
| 471 | |
| 472 case WebContextMenuData::MediaTypeVideo: | |
| 473 if (contexts.Contains(MenuItem::VIDEO) && | |
| 474 ExtensionPatternMatch(target_url_patterns, params.src_url)) | |
| 475 return true; | |
| 476 break; | |
| 477 | |
| 478 case WebContextMenuData::MediaTypeAudio: | |
| 479 if (contexts.Contains(MenuItem::AUDIO) && | |
| 480 ExtensionPatternMatch(target_url_patterns, params.src_url)) | |
| 481 return true; | |
| 482 break; | |
| 483 | |
| 484 default: | |
| 485 break; | |
| 486 } | |
| 487 | |
| 488 // PAGE is the least specific context, so we only examine that if none of the | |
| 489 // other contexts apply (except for FRAME, which is included in PAGE for | |
| 490 // backwards compatibility). | |
| 491 if (!has_link && !has_selection && !params.is_editable && | |
| 492 params.media_type == WebContextMenuData::MediaTypeNone && | |
| 493 contexts.Contains(MenuItem::PAGE)) | |
| 494 return true; | |
| 495 | |
| 496 return false; | |
| 497 } | |
| 498 | |
| 499 static const GURL& GetDocumentURL(const content::ContextMenuParams& params) { | |
| 500 return params.frame_url.is_empty() ? params.page_url : params.frame_url; | |
| 501 } | |
| 502 | |
| 503 // static | |
| 504 bool RenderViewContextMenu::MenuItemMatchesParams( | |
| 505 const content::ContextMenuParams& params, | |
| 506 const extensions::MenuItem* item) { | |
| 507 bool match = ExtensionContextAndPatternMatch(params, item->contexts(), | |
| 508 item->target_url_patterns()); | |
| 509 if (!match) | |
| 510 return false; | |
| 511 | |
| 512 const GURL& document_url = GetDocumentURL(params); | |
| 513 return ExtensionPatternMatch(item->document_url_patterns(), document_url); | |
| 514 } | |
| 515 | |
| 516 void RenderViewContextMenu::AppendAllExtensionItems() { | |
| 517 extension_items_.Clear(); | |
| 518 ExtensionService* service = | |
| 519 extensions::ExtensionSystem::Get(profile_)->extension_service(); | |
| 520 if (!service) | |
| 521 return; // In unit-tests, we may not have an ExtensionService. | |
| 522 | |
| 523 MenuManager* menu_manager = MenuManager::Get(profile_); | |
| 524 if (!menu_manager) | |
| 525 return; | |
| 526 | |
| 527 base::string16 printable_selection_text = PrintableSelectionText(); | |
| 528 EscapeAmpersands(&printable_selection_text); | |
| 529 | |
| 530 // Get a list of extension id's that have context menu items, and sort by the | |
| 531 // top level context menu title of the extension. | |
| 532 std::set<std::string> ids = menu_manager->ExtensionIds(); | |
| 533 std::vector<base::string16> sorted_menu_titles; | |
| 534 std::map<base::string16, std::string> map_ids; | |
| 535 for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) { | |
| 536 const Extension* extension = service->GetExtensionById(*i, false); | |
| 537 // Platform apps have their context menus created directly in | |
| 538 // AppendPlatformAppItems. | |
| 539 if (extension && !extension->is_platform_app()) { | |
| 540 base::string16 menu_title = extension_items_.GetTopLevelContextMenuTitle( | |
| 541 *i, printable_selection_text); | |
| 542 map_ids[menu_title] = *i; | |
| 543 sorted_menu_titles.push_back(menu_title); | |
| 544 } | |
| 545 } | |
| 546 if (sorted_menu_titles.empty()) | |
| 547 return; | |
| 548 | |
| 549 const std::string app_locale = g_browser_process->GetApplicationLocale(); | |
| 550 l10n_util::SortStrings16(app_locale, &sorted_menu_titles); | |
| 551 | |
| 552 int index = 0; | |
| 553 base::TimeTicks begin = base::TimeTicks::Now(); | |
| 554 for (size_t i = 0; i < sorted_menu_titles.size(); ++i) { | |
| 555 const std::string& id = map_ids[sorted_menu_titles[i]]; | |
| 556 extension_items_.AppendExtensionItems(id, printable_selection_text, | |
| 557 &index); | |
| 558 } | |
| 559 | |
| 560 UMA_HISTOGRAM_TIMES("Extensions.ContextMenus_BuildTime", | |
| 561 base::TimeTicks::Now() - begin); | |
| 562 UMA_HISTOGRAM_COUNTS("Extensions.ContextMenus_ItemCount", index); | |
| 563 } | |
| 564 | |
| 565 void RenderViewContextMenu::InitMenu() { | |
| 566 if (chrome::IsRunningInForcedAppMode()) { | |
| 567 AppendAppModeItems(); | |
| 568 return; | |
| 569 } | |
| 570 | |
| 571 extensions::ViewType view_type = | |
| 572 extensions::GetViewType(source_web_contents_); | |
| 573 if (view_type == extensions::VIEW_TYPE_APP_WINDOW) { | |
| 574 AppendPlatformAppItems(); | |
| 575 return; | |
| 576 } else if (view_type == extensions::VIEW_TYPE_EXTENSION_POPUP) { | |
| 577 AppendPopupExtensionItems(); | |
| 578 return; | |
| 579 } else if (view_type == extensions::VIEW_TYPE_PANEL) { | |
| 580 AppendPanelItems(); | |
| 581 return; | |
| 582 } | |
| 583 | |
| 584 const bool has_link = !params_.unfiltered_link_url.is_empty(); | |
| 585 const bool has_selection = !params_.selection_text.empty(); | |
| 586 | |
| 587 if (AppendCustomItems()) { | |
| 588 // If there's a selection, don't early return when there are custom items, | |
| 589 // but fall through to adding the normal ones after the custom ones. | |
| 590 if (has_selection) { | |
| 591 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 592 } else { | |
| 593 // Don't add items for Pepper menu. | |
| 594 if (!params_.custom_context.is_pepper_menu) | |
| 595 AppendDeveloperItems(); | |
| 596 return; | |
| 597 } | |
| 598 } | |
| 599 | |
| 600 // When no special node or text is selected and selection has no link, | |
| 601 // show page items. | |
| 602 if (params_.media_type == WebContextMenuData::MediaTypeNone && | |
| 603 !has_link && | |
| 604 !params_.is_editable && | |
| 605 !is_guest_ && | |
| 606 !has_selection) { | |
| 607 if (!params_.page_url.is_empty()) { | |
| 608 bool is_devtools = IsDevToolsURL(params_.page_url); | |
| 609 if (!is_devtools && !IsInternalResourcesURL(params_.page_url)) { | |
| 610 AppendPageItems(); | |
| 611 // Merge in frame items if we clicked within a frame that needs them. | |
| 612 if (!params_.frame_url.is_empty()) { | |
| 613 is_devtools = IsDevToolsURL(params_.frame_url); | |
| 614 if (!is_devtools && !IsInternalResourcesURL(params_.frame_url)) { | |
| 615 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 616 AppendFrameItems(); | |
| 617 } | |
| 618 } | |
| 619 } | |
| 620 } else { | |
| 621 DCHECK(params_.frame_url.is_empty()); | |
| 622 } | |
| 623 } | |
| 624 | |
| 625 // Do not show link related items for guest. | |
| 626 if (has_link && !is_guest_) { | |
| 627 AppendLinkItems(); | |
| 628 if (params_.media_type != WebContextMenuData::MediaTypeNone) | |
| 629 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 630 } | |
| 631 | |
| 632 switch (params_.media_type) { | |
| 633 case WebContextMenuData::MediaTypeNone: | |
| 634 break; | |
| 635 case WebContextMenuData::MediaTypeImage: | |
| 636 AppendImageItems(); | |
| 637 break; | |
| 638 case WebContextMenuData::MediaTypeVideo: | |
| 639 AppendVideoItems(); | |
| 640 break; | |
| 641 case WebContextMenuData::MediaTypeAudio: | |
| 642 AppendAudioItems(); | |
| 643 break; | |
| 644 case WebContextMenuData::MediaTypePlugin: | |
| 645 AppendPluginItems(); | |
| 646 break; | |
| 647 #ifdef WEBCONTEXT_MEDIATYPEFILE_DEFINED | |
| 648 case WebContextMenuData::MediaTypeFile: | |
| 649 break; | |
| 650 #endif | |
| 651 } | |
| 652 | |
| 653 if (params_.is_editable) | |
| 654 AppendEditableItems(); | |
| 655 else if (has_selection) | |
| 656 AppendCopyItem(); | |
| 657 | |
| 658 if (!is_guest_ && has_selection) { | |
| 659 AppendSearchProvider(); | |
| 660 if (!IsDevToolsURL(params_.page_url)) | |
| 661 AppendPrintItem(); | |
| 662 } | |
| 663 | |
| 664 if (!IsDevToolsURL(params_.page_url) && !is_guest_) | |
| 665 AppendAllExtensionItems(); | |
| 666 | |
| 667 AppendDeveloperItems(); | |
| 668 | |
| 669 if (!is_guest_) { | |
| 670 #if defined(ENABLE_FULL_PRINTING) | |
| 671 if (!print_preview_menu_observer_.get()) { | |
| 672 print_preview_menu_observer_.reset( | |
| 673 new PrintPreviewContextMenuObserver(source_web_contents_)); | |
| 674 } | |
| 675 | |
| 676 observers_.AddObserver(print_preview_menu_observer_.get()); | |
| 677 #endif | |
| 678 } | |
| 679 } | |
| 680 | |
| 681 const Extension* RenderViewContextMenu::GetExtension() const { | |
| 682 extensions::ExtensionSystem* system = | |
| 683 extensions::ExtensionSystem::Get(profile_); | |
| 684 // There is no process manager in some tests. | |
| 685 if (!system->process_manager()) | |
| 686 return NULL; | |
| 687 | |
| 688 return system->process_manager()->GetExtensionForRenderViewHost( | |
| 689 source_web_contents_->GetRenderViewHost()); | |
| 690 } | |
| 691 | |
| 692 void RenderViewContextMenu::AppendAppModeItems() { | |
| 693 const bool has_selection = !params_.selection_text.empty(); | |
| 694 | |
| 695 if (params_.is_editable) | |
| 696 AppendEditableItems(); | |
| 697 else if (has_selection) | |
| 698 AppendCopyItem(); | |
| 699 } | |
| 700 | |
| 701 void RenderViewContextMenu::AppendPlatformAppItems() { | |
| 702 const Extension* platform_app = GetExtension(); | |
| 703 | |
| 704 // The RVH might be for a process sandboxed from the extension. | |
| 705 if (!platform_app) | |
| 706 return; | |
| 707 | |
| 708 DCHECK(platform_app->is_platform_app()); | |
| 709 | |
| 710 const bool has_selection = !params_.selection_text.empty(); | |
| 711 | |
| 712 // Add undo/redo, cut/copy/paste etc for text fields. | |
| 713 if (params_.is_editable) | |
| 714 AppendEditableItems(); | |
| 715 else if (has_selection) | |
| 716 AppendCopyItem(); | |
| 717 | |
| 718 int index = 0; | |
| 719 extension_items_.AppendExtensionItems(platform_app->id(), | |
| 720 PrintableSelectionText(), &index); | |
| 721 | |
| 722 // Add dev tools for unpacked extensions. | |
| 723 if (extensions::Manifest::IsUnpackedLocation(platform_app->location()) || | |
| 724 CommandLine::ForCurrentProcess()->HasSwitch( | |
| 725 switches::kDebugPackedApps)) { | |
| 726 // Add a separator if there are any items already in the menu. | |
| 727 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 728 | |
| 729 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP, | |
| 730 IDS_CONTENT_CONTEXT_RELOAD_PACKAGED_APP); | |
| 731 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP, | |
| 732 IDS_CONTENT_CONTEXT_RESTART_APP); | |
| 733 AppendDeveloperItems(); | |
| 734 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE, | |
| 735 IDS_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE); | |
| 736 } | |
| 737 } | |
| 738 | |
| 739 void RenderViewContextMenu::AppendPopupExtensionItems() { | |
| 740 const bool has_selection = !params_.selection_text.empty(); | |
| 741 | |
| 742 if (params_.is_editable) | |
| 743 AppendEditableItems(); | |
| 744 else if (has_selection) | |
| 745 AppendCopyItem(); | |
| 746 | |
| 747 if (has_selection) | |
| 748 AppendSearchProvider(); | |
| 749 | |
| 750 AppendAllExtensionItems(); | |
| 751 AppendDeveloperItems(); | |
| 752 } | |
| 753 | |
| 754 void RenderViewContextMenu::AppendPanelItems() { | |
| 755 bool has_selection = !params_.selection_text.empty(); | |
| 756 | |
| 757 // Checking link should take precedence before checking selection since on Mac | |
| 758 // right-clicking a link will also make it selected. | |
| 759 if (params_.unfiltered_link_url.is_valid()) | |
| 760 AppendLinkItems(); | |
| 761 | |
| 762 if (params_.is_editable) | |
| 763 AppendEditableItems(); | |
| 764 else if (has_selection) | |
| 765 AppendCopyItem(); | |
| 766 | |
| 767 // Avoid appending extension related items when |extension| is null. This | |
| 768 // happens when the panel is navigated to a url outside of the extension's | |
| 769 // package. | |
| 770 const Extension* extension = GetExtension(); | |
| 771 if (extension) { | |
| 772 // Only add extension items from this extension. | |
| 773 int index = 0; | |
| 774 extension_items_.AppendExtensionItems(extension->id(), | |
| 775 PrintableSelectionText(), &index); | |
| 776 } | |
| 777 } | |
| 778 | |
| 779 void RenderViewContextMenu::AddMenuItem(int command_id, | |
| 780 const base::string16& title) { | |
| 781 menu_model_.AddItem(command_id, title); | |
| 782 } | |
| 783 | |
| 784 void RenderViewContextMenu::AddCheckItem(int command_id, | |
| 785 const base::string16& title) { | |
| 786 menu_model_.AddCheckItem(command_id, title); | |
| 787 } | |
| 788 | |
| 789 void RenderViewContextMenu::AddSeparator() { | |
| 790 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 791 } | |
| 792 | |
| 793 void RenderViewContextMenu::AddSubMenu(int command_id, | |
| 794 const base::string16& label, | |
| 795 ui::MenuModel* model) { | |
| 796 menu_model_.AddSubMenu(command_id, label, model); | |
| 797 } | |
| 798 | |
| 799 void RenderViewContextMenu::UpdateMenuItem(int command_id, | |
| 800 bool enabled, | |
| 801 bool hidden, | |
| 802 const base::string16& label) { | |
| 803 // This function needs platform-specific implementation. | |
| 804 NOTIMPLEMENTED(); | |
| 805 } | |
| 806 | |
| 807 RenderViewHost* RenderViewContextMenu::GetRenderViewHost() const { | |
| 808 return source_web_contents_->GetRenderViewHost(); | |
| 809 } | |
| 810 | |
| 811 WebContents* RenderViewContextMenu::GetWebContents() const { | |
| 812 return source_web_contents_; | |
| 813 } | |
| 814 | |
| 815 Profile* RenderViewContextMenu::GetProfile() const { | |
| 816 return profile_; | |
| 817 } | |
| 818 | |
| 819 bool RenderViewContextMenu::AppendCustomItems() { | |
| 820 size_t total_items = 0; | |
| 821 AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this, | |
| 822 &menu_model_); | |
| 823 return total_items > 0; | |
| 824 } | |
| 825 | |
| 826 void RenderViewContextMenu::AppendDeveloperItems() { | |
| 827 // Show Inspect Element in DevTools itself only in case of the debug | |
| 828 // devtools build. | |
| 829 bool show_developer_items = !IsDevToolsURL(params_.page_url); | |
| 830 | |
| 831 #if defined(DEBUG_DEVTOOLS) | |
| 832 show_developer_items = true; | |
| 833 #endif | |
| 834 | |
| 835 if (!show_developer_items) | |
| 836 return; | |
| 837 | |
| 838 // In the DevTools popup menu, "developer items" is normally the only | |
| 839 // section, so omit the separator there. | |
| 840 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 841 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTELEMENT, | |
| 842 IDS_CONTENT_CONTEXT_INSPECTELEMENT); | |
| 843 } | |
| 844 | |
| 845 void RenderViewContextMenu::AppendLinkItems() { | |
| 846 if (!params_.link_url.is_empty()) { | |
| 847 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB, | |
| 848 IDS_CONTENT_CONTEXT_OPENLINKNEWTAB); | |
| 849 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW, | |
| 850 IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW); | |
| 851 if (params_.link_url.is_valid()) { | |
| 852 AppendProtocolHandlerSubMenu(); | |
| 853 } | |
| 854 | |
| 855 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD, | |
| 856 IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD); | |
| 857 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVELINKAS, | |
| 858 IDS_CONTENT_CONTEXT_SAVELINKAS); | |
| 859 } | |
| 860 | |
| 861 menu_model_.AddItemWithStringId( | |
| 862 IDC_CONTENT_CONTEXT_COPYLINKLOCATION, | |
| 863 params_.link_url.SchemeIs(content::kMailToScheme) ? | |
| 864 IDS_CONTENT_CONTEXT_COPYEMAILADDRESS : | |
| 865 IDS_CONTENT_CONTEXT_COPYLINKLOCATION); | |
| 866 } | |
| 867 | |
| 868 void RenderViewContextMenu::AppendImageItems() { | |
| 869 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, | |
| 870 IDS_CONTENT_CONTEXT_SAVEIMAGEAS); | |
| 871 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION, | |
| 872 IDS_CONTENT_CONTEXT_COPYIMAGELOCATION); | |
| 873 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGE, | |
| 874 IDS_CONTENT_CONTEXT_COPYIMAGE); | |
| 875 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB, | |
| 876 IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB); | |
| 877 const TemplateURL* const default_provider = | |
| 878 TemplateURLServiceFactory::GetForProfile(profile_)-> | |
| 879 GetDefaultSearchProvider(); | |
| 880 if (!is_guest_ && params_.has_image_contents && default_provider && | |
| 881 !default_provider->image_url().empty() && | |
| 882 default_provider->image_url_ref().IsValid()) { | |
| 883 menu_model_.AddItem( | |
| 884 IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE, | |
| 885 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE, | |
| 886 default_provider->short_name())); | |
| 887 } | |
| 888 AppendPrintItem(); | |
| 889 } | |
| 890 | |
| 891 void RenderViewContextMenu::AppendAudioItems() { | |
| 892 AppendMediaItems(); | |
| 893 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 894 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, | |
| 895 IDS_CONTENT_CONTEXT_SAVEAUDIOAS); | |
| 896 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, | |
| 897 IDS_CONTENT_CONTEXT_COPYAUDIOLOCATION); | |
| 898 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, | |
| 899 IDS_CONTENT_CONTEXT_OPENAUDIONEWTAB); | |
| 900 } | |
| 901 | |
| 902 void RenderViewContextMenu::AppendVideoItems() { | |
| 903 AppendMediaItems(); | |
| 904 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 905 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, | |
| 906 IDS_CONTENT_CONTEXT_SAVEVIDEOAS); | |
| 907 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYAVLOCATION, | |
| 908 IDS_CONTENT_CONTEXT_COPYVIDEOLOCATION); | |
| 909 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, | |
| 910 IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB); | |
| 911 } | |
| 912 | |
| 913 void RenderViewContextMenu::AppendMediaItems() { | |
| 914 int media_flags = params_.media_flags; | |
| 915 | |
| 916 menu_model_.AddItemWithStringId( | |
| 917 IDC_CONTENT_CONTEXT_PLAYPAUSE, | |
| 918 media_flags & WebContextMenuData::MediaPaused ? | |
| 919 IDS_CONTENT_CONTEXT_PLAY : | |
| 920 IDS_CONTENT_CONTEXT_PAUSE); | |
| 921 | |
| 922 menu_model_.AddItemWithStringId( | |
| 923 IDC_CONTENT_CONTEXT_MUTE, | |
| 924 media_flags & WebContextMenuData::MediaMuted ? | |
| 925 IDS_CONTENT_CONTEXT_UNMUTE : | |
| 926 IDS_CONTENT_CONTEXT_MUTE); | |
| 927 | |
| 928 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_LOOP, | |
| 929 IDS_CONTENT_CONTEXT_LOOP); | |
| 930 menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_CONTROLS, | |
| 931 IDS_CONTENT_CONTEXT_CONTROLS); | |
| 932 } | |
| 933 | |
| 934 void RenderViewContextMenu::AppendPluginItems() { | |
| 935 if (params_.page_url == params_.src_url) { | |
| 936 // Full page plugin, so show page menu items. | |
| 937 if (params_.link_url.is_empty() && params_.selection_text.empty()) | |
| 938 AppendPageItems(); | |
| 939 } else { | |
| 940 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS, | |
| 941 IDS_CONTENT_CONTEXT_SAVEPAGEAS); | |
| 942 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT); | |
| 943 } | |
| 944 | |
| 945 if (params_.media_flags & WebContextMenuData::MediaCanRotate) { | |
| 946 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 947 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECW, | |
| 948 IDS_CONTENT_CONTEXT_ROTATECW); | |
| 949 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ROTATECCW, | |
| 950 IDS_CONTENT_CONTEXT_ROTATECCW); | |
| 951 } | |
| 952 } | |
| 953 | |
| 954 void RenderViewContextMenu::AppendPageItems() { | |
| 955 menu_model_.AddItemWithStringId(IDC_BACK, IDS_CONTENT_CONTEXT_BACK); | |
| 956 menu_model_.AddItemWithStringId(IDC_FORWARD, IDS_CONTENT_CONTEXT_FORWARD); | |
| 957 menu_model_.AddItemWithStringId(IDC_RELOAD, IDS_CONTENT_CONTEXT_RELOAD); | |
| 958 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 959 menu_model_.AddItemWithStringId(IDC_SAVE_PAGE, | |
| 960 IDS_CONTENT_CONTEXT_SAVEPAGEAS); | |
| 961 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT); | |
| 962 | |
| 963 if (TranslateManager::IsTranslatableURL(params_.page_url)) { | |
| 964 std::string locale = g_browser_process->GetApplicationLocale(); | |
| 965 locale = TranslateDownloadManager::GetLanguageCode(locale); | |
| 966 base::string16 language = | |
| 967 l10n_util::GetDisplayNameForLocale(locale, locale, true); | |
| 968 menu_model_.AddItem( | |
| 969 IDC_CONTENT_CONTEXT_TRANSLATE, | |
| 970 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE, language)); | |
| 971 } | |
| 972 | |
| 973 menu_model_.AddItemWithStringId(IDC_VIEW_SOURCE, | |
| 974 IDS_CONTENT_CONTEXT_VIEWPAGESOURCE); | |
| 975 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWPAGEINFO, | |
| 976 IDS_CONTENT_CONTEXT_VIEWPAGEINFO); | |
| 977 } | |
| 978 | |
| 979 void RenderViewContextMenu::AppendFrameItems() { | |
| 980 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOADFRAME, | |
| 981 IDS_CONTENT_CONTEXT_RELOADFRAME); | |
| 982 // These two menu items have yet to be implemented. | |
| 983 // http://code.google.com/p/chromium/issues/detail?id=11827 | |
| 984 // IDS_CONTENT_CONTEXT_SAVEFRAMEAS | |
| 985 // IDS_CONTENT_CONTEXT_PRINTFRAME | |
| 986 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE, | |
| 987 IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE); | |
| 988 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_VIEWFRAMEINFO, | |
| 989 IDS_CONTENT_CONTEXT_VIEWFRAMEINFO); | |
| 990 } | |
| 991 | |
| 992 void RenderViewContextMenu::AppendCopyItem() { | |
| 993 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY, | |
| 994 IDS_CONTENT_CONTEXT_COPY); | |
| 995 } | |
| 996 | |
| 997 void RenderViewContextMenu::AppendPrintItem() { | |
| 998 if (profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) && | |
| 999 (params_.media_type == WebContextMenuData::MediaTypeNone || | |
| 1000 params_.media_flags & WebContextMenuData::MediaCanPrint)) { | |
| 1001 menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT); | |
| 1002 } | |
| 1003 } | |
| 1004 | |
| 1005 void RenderViewContextMenu::AppendSearchProvider() { | |
| 1006 DCHECK(profile_); | |
| 1007 | |
| 1008 TrimWhitespace(params_.selection_text, TRIM_ALL, ¶ms_.selection_text); | |
| 1009 if (params_.selection_text.empty()) | |
| 1010 return; | |
| 1011 | |
| 1012 base::ReplaceChars(params_.selection_text, AutocompleteMatch::kInvalidChars, | |
| 1013 base::ASCIIToUTF16(" "), ¶ms_.selection_text); | |
| 1014 | |
| 1015 AutocompleteMatch match; | |
| 1016 AutocompleteClassifierFactory::GetForProfile(profile_)->Classify( | |
| 1017 params_.selection_text, false, false, AutocompleteInput::INVALID_SPEC, | |
| 1018 &match, NULL); | |
| 1019 selection_navigation_url_ = match.destination_url; | |
| 1020 if (!selection_navigation_url_.is_valid()) | |
| 1021 return; | |
| 1022 | |
| 1023 base::string16 printable_selection_text = PrintableSelectionText(); | |
| 1024 EscapeAmpersands(&printable_selection_text); | |
| 1025 | |
| 1026 if (AutocompleteMatch::IsSearchType(match.type)) { | |
| 1027 const TemplateURL* const default_provider = | |
| 1028 TemplateURLServiceFactory::GetForProfile(profile_)-> | |
| 1029 GetDefaultSearchProvider(); | |
| 1030 if (!default_provider) | |
| 1031 return; | |
| 1032 menu_model_.AddItem( | |
| 1033 IDC_CONTENT_CONTEXT_SEARCHWEBFOR, | |
| 1034 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR, | |
| 1035 default_provider->short_name(), | |
| 1036 printable_selection_text)); | |
| 1037 } else { | |
| 1038 if ((selection_navigation_url_ != params_.link_url) && | |
| 1039 ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme( | |
| 1040 selection_navigation_url_.scheme())) { | |
| 1041 menu_model_.AddItem( | |
| 1042 IDC_CONTENT_CONTEXT_GOTOURL, | |
| 1043 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_GOTOURL, | |
| 1044 printable_selection_text)); | |
| 1045 } | |
| 1046 } | |
| 1047 } | |
| 1048 | |
| 1049 void RenderViewContextMenu::AppendEditableItems() { | |
| 1050 const bool use_spellcheck_and_search = !chrome::IsRunningInForcedAppMode(); | |
| 1051 | |
| 1052 if (use_spellcheck_and_search) | |
| 1053 AppendSpellingSuggestionsSubMenu(); | |
| 1054 | |
| 1055 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_UNDO, | |
| 1056 IDS_CONTENT_CONTEXT_UNDO); | |
| 1057 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_REDO, | |
| 1058 IDS_CONTENT_CONTEXT_REDO); | |
| 1059 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 1060 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_CUT, | |
| 1061 IDS_CONTENT_CONTEXT_CUT); | |
| 1062 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPY, | |
| 1063 IDS_CONTENT_CONTEXT_COPY); | |
| 1064 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE, | |
| 1065 IDS_CONTENT_CONTEXT_PASTE); | |
| 1066 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE, | |
| 1067 IDS_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE); | |
| 1068 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_DELETE, | |
| 1069 IDS_CONTENT_CONTEXT_DELETE); | |
| 1070 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 1071 | |
| 1072 if (use_spellcheck_and_search && !params_.keyword_url.is_empty()) { | |
| 1073 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ADDSEARCHENGINE, | |
| 1074 IDS_CONTENT_CONTEXT_ADDSEARCHENGINE); | |
| 1075 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 1076 } | |
| 1077 | |
| 1078 if (use_spellcheck_and_search) | |
| 1079 AppendSpellcheckOptionsSubMenu(); | |
| 1080 AppendSpeechInputOptionsSubMenu(); | |
| 1081 AppendPlatformEditableItems(); | |
| 1082 | |
| 1083 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 1084 menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SELECTALL, | |
| 1085 IDS_CONTENT_CONTEXT_SELECTALL); | |
| 1086 } | |
| 1087 | |
| 1088 void RenderViewContextMenu::AppendSpellingSuggestionsSubMenu() { | |
| 1089 if (!spelling_menu_observer_.get()) | |
| 1090 spelling_menu_observer_.reset(new SpellingMenuObserver(this)); | |
| 1091 observers_.AddObserver(spelling_menu_observer_.get()); | |
| 1092 spelling_menu_observer_->InitMenu(params_); | |
| 1093 } | |
| 1094 | |
| 1095 void RenderViewContextMenu::AppendSpellcheckOptionsSubMenu() { | |
| 1096 if (!spellchecker_submenu_observer_.get()) { | |
| 1097 spellchecker_submenu_observer_.reset(new SpellCheckerSubMenuObserver( | |
| 1098 this, this, kSpellcheckRadioGroup)); | |
| 1099 } | |
| 1100 spellchecker_submenu_observer_->InitMenu(params_); | |
| 1101 observers_.AddObserver(spellchecker_submenu_observer_.get()); | |
| 1102 } | |
| 1103 | |
| 1104 void RenderViewContextMenu::AppendSpeechInputOptionsSubMenu() { | |
| 1105 if (params_.speech_input_enabled) { | |
| 1106 speech_input_submenu_model_.AddCheckItem( | |
| 1107 IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES, | |
| 1108 l10n_util::GetStringUTF16( | |
| 1109 IDS_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES)); | |
| 1110 | |
| 1111 speech_input_submenu_model_.AddItemWithStringId( | |
| 1112 IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT, | |
| 1113 IDS_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT); | |
| 1114 | |
| 1115 menu_model_.AddSubMenu( | |
| 1116 IDC_SPEECH_INPUT_MENU, | |
| 1117 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPEECH_INPUT_MENU), | |
| 1118 &speech_input_submenu_model_); | |
| 1119 } | |
| 1120 } | |
| 1121 | |
| 1122 void RenderViewContextMenu::AppendProtocolHandlerSubMenu() { | |
| 1123 const ProtocolHandlerRegistry::ProtocolHandlerList handlers = | |
| 1124 GetHandlersForLinkUrl(); | |
| 1125 if (handlers.empty()) | |
| 1126 return; | |
| 1127 size_t max = IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST - | |
| 1128 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST; | |
| 1129 for (size_t i = 0; i < handlers.size() && i <= max; i++) { | |
| 1130 protocol_handler_submenu_model_.AddItem( | |
| 1131 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST + i, | |
| 1132 handlers[i].title()); | |
| 1133 } | |
| 1134 protocol_handler_submenu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 1135 protocol_handler_submenu_model_.AddItem( | |
| 1136 IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS, | |
| 1137 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH_CONFIGURE)); | |
| 1138 | |
| 1139 menu_model_.AddSubMenu( | |
| 1140 IDC_CONTENT_CONTEXT_OPENLINKWITH, | |
| 1141 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_OPENLINKWITH), | |
| 1142 &protocol_handler_submenu_model_); | |
| 1143 } | |
| 1144 | |
| 1145 void RenderViewContextMenu::AppendPlatformEditableItems() { | |
| 1146 } | |
| 1147 | |
| 1148 // Menu delegate functions ----------------------------------------------------- | |
| 1149 | |
| 1150 bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { | |
| 1151 // If this command is is added by one of our observers, we dispatch it to the | |
| 1152 // observer. | |
| 1153 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); | |
| 1154 RenderViewContextMenuObserver* observer; | |
| 1155 while ((observer = it.GetNext()) != NULL) { | |
| 1156 if (observer->IsCommandIdSupported(id)) | |
| 1157 return observer->IsCommandIdEnabled(id); | |
| 1158 } | |
| 1159 | |
| 1160 CoreTabHelper* core_tab_helper = | |
| 1161 CoreTabHelper::FromWebContents(source_web_contents_); | |
| 1162 int content_restrictions = 0; | |
| 1163 if (core_tab_helper) | |
| 1164 content_restrictions = core_tab_helper->content_restrictions(); | |
| 1165 if (id == IDC_PRINT && (content_restrictions & CONTENT_RESTRICTION_PRINT)) | |
| 1166 return false; | |
| 1167 | |
| 1168 if (id == IDC_SAVE_PAGE && | |
| 1169 (content_restrictions & CONTENT_RESTRICTION_SAVE)) { | |
| 1170 return false; | |
| 1171 } | |
| 1172 | |
| 1173 // Allow Spell Check language items on sub menu for text area context menu. | |
| 1174 if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) && | |
| 1175 (id < IDC_SPELLCHECK_LANGUAGES_LAST)) { | |
| 1176 return profile_->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck); | |
| 1177 } | |
| 1178 | |
| 1179 // Custom items. | |
| 1180 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && | |
| 1181 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { | |
| 1182 return IsCustomItemEnabled(params_.custom_items, id); | |
| 1183 } | |
| 1184 | |
| 1185 // Extension items. | |
| 1186 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && | |
| 1187 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { | |
| 1188 return extension_items_.IsCommandIdEnabled(id); | |
| 1189 } | |
| 1190 | |
| 1191 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST && | |
| 1192 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) { | |
| 1193 return true; | |
| 1194 } | |
| 1195 | |
| 1196 IncognitoModePrefs::Availability incognito_avail = | |
| 1197 IncognitoModePrefs::GetAvailability(profile_->GetPrefs()); | |
| 1198 switch (id) { | |
| 1199 case IDC_BACK: | |
| 1200 return source_web_contents_->GetController().CanGoBack(); | |
| 1201 | |
| 1202 case IDC_FORWARD: | |
| 1203 return source_web_contents_->GetController().CanGoForward(); | |
| 1204 | |
| 1205 case IDC_RELOAD: { | |
| 1206 CoreTabHelper* core_tab_helper = | |
| 1207 CoreTabHelper::FromWebContents(source_web_contents_); | |
| 1208 if (!core_tab_helper) | |
| 1209 return false; | |
| 1210 | |
| 1211 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate(); | |
| 1212 return !core_delegate || | |
| 1213 core_delegate->CanReloadContents(source_web_contents_); | |
| 1214 } | |
| 1215 | |
| 1216 case IDC_VIEW_SOURCE: | |
| 1217 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE: | |
| 1218 return source_web_contents_->GetController().CanViewSource(); | |
| 1219 | |
| 1220 case IDC_CONTENT_CONTEXT_INSPECTELEMENT: | |
| 1221 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE: | |
| 1222 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP: | |
| 1223 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP: | |
| 1224 return IsDevCommandEnabled(id); | |
| 1225 | |
| 1226 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: | |
| 1227 if (source_web_contents_->GetController().GetActiveEntry() == NULL) | |
| 1228 return false; | |
| 1229 // Disabled if no browser is associated (e.g. desktop notifications). | |
| 1230 if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL) | |
| 1231 return false; | |
| 1232 return true; | |
| 1233 | |
| 1234 case IDC_CONTENT_CONTEXT_TRANSLATE: { | |
| 1235 TranslateTabHelper* translate_tab_helper = | |
| 1236 TranslateTabHelper::FromWebContents(source_web_contents_); | |
| 1237 if (!translate_tab_helper) | |
| 1238 return false; | |
| 1239 std::string original_lang = | |
| 1240 translate_tab_helper->GetLanguageState().original_language(); | |
| 1241 std::string target_lang = g_browser_process->GetApplicationLocale(); | |
| 1242 target_lang = TranslateDownloadManager::GetLanguageCode(target_lang); | |
| 1243 // Note that we intentionally enable the menu even if the original and | |
| 1244 // target languages are identical. This is to give a way to user to | |
| 1245 // translate a page that might contains text fragments in a different | |
| 1246 // language. | |
| 1247 return ((params_.edit_flags & WebContextMenuData::CanTranslate) != 0) && | |
| 1248 !original_lang.empty() && // Did we receive the page language yet? | |
| 1249 !translate_tab_helper->GetLanguageState().IsPageTranslated() && | |
| 1250 !source_web_contents_->GetInterstitialPage() && | |
| 1251 // There are some application locales which can't be used as a | |
| 1252 // target language for translation. | |
| 1253 TranslateDownloadManager::IsSupportedLanguage(target_lang) && | |
| 1254 // Disable on the Instant Extended NTP. | |
| 1255 !chrome::IsInstantNTP(source_web_contents_); | |
| 1256 } | |
| 1257 | |
| 1258 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: | |
| 1259 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW: | |
| 1260 return params_.link_url.is_valid(); | |
| 1261 | |
| 1262 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION: | |
| 1263 return params_.unfiltered_link_url.is_valid(); | |
| 1264 | |
| 1265 case IDC_CONTENT_CONTEXT_SAVELINKAS: { | |
| 1266 PrefService* local_state = g_browser_process->local_state(); | |
| 1267 DCHECK(local_state); | |
| 1268 // Test if file-selection dialogs are forbidden by policy. | |
| 1269 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs)) | |
| 1270 return false; | |
| 1271 | |
| 1272 return params_.link_url.is_valid() && | |
| 1273 ProfileIOData::IsHandledProtocol(params_.link_url.scheme()); | |
| 1274 } | |
| 1275 | |
| 1276 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: { | |
| 1277 PrefService* local_state = g_browser_process->local_state(); | |
| 1278 DCHECK(local_state); | |
| 1279 // Test if file-selection dialogs are forbidden by policy. | |
| 1280 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs)) | |
| 1281 return false; | |
| 1282 | |
| 1283 return params_.src_url.is_valid() && | |
| 1284 ProfileIOData::IsHandledProtocol(params_.src_url.scheme()); | |
| 1285 } | |
| 1286 | |
| 1287 // The images shown in the most visited thumbnails can't be opened or | |
| 1288 // searched for conventionally. | |
| 1289 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB: | |
| 1290 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE: | |
| 1291 return params_.src_url.is_valid() && | |
| 1292 (params_.src_url.scheme() != content::kChromeUIScheme); | |
| 1293 | |
| 1294 case IDC_CONTENT_CONTEXT_COPYIMAGE: | |
| 1295 return params_.has_image_contents; | |
| 1296 | |
| 1297 // Media control commands should all be disabled if the player is in an | |
| 1298 // error state. | |
| 1299 case IDC_CONTENT_CONTEXT_PLAYPAUSE: | |
| 1300 case IDC_CONTENT_CONTEXT_LOOP: | |
| 1301 return (params_.media_flags & | |
| 1302 WebContextMenuData::MediaInError) == 0; | |
| 1303 | |
| 1304 // Mute and unmute should also be disabled if the player has no audio. | |
| 1305 case IDC_CONTENT_CONTEXT_MUTE: | |
| 1306 return (params_.media_flags & | |
| 1307 WebContextMenuData::MediaHasAudio) != 0 && | |
| 1308 (params_.media_flags & | |
| 1309 WebContextMenuData::MediaInError) == 0; | |
| 1310 | |
| 1311 // Media controls can be toggled only for video player. If we toggle | |
| 1312 // controls for audio then the player disappears, and there is no way to | |
| 1313 // return it back. | |
| 1314 case IDC_CONTENT_CONTEXT_CONTROLS: | |
| 1315 return (params_.media_flags & | |
| 1316 WebContextMenuData::MediaHasVideo) != 0; | |
| 1317 | |
| 1318 case IDC_CONTENT_CONTEXT_ROTATECW: | |
| 1319 case IDC_CONTENT_CONTEXT_ROTATECCW: | |
| 1320 return | |
| 1321 (params_.media_flags & WebContextMenuData::MediaCanRotate) != 0; | |
| 1322 | |
| 1323 case IDC_CONTENT_CONTEXT_COPYAVLOCATION: | |
| 1324 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION: | |
| 1325 return params_.src_url.is_valid(); | |
| 1326 | |
| 1327 case IDC_CONTENT_CONTEXT_SAVEAVAS: { | |
| 1328 PrefService* local_state = g_browser_process->local_state(); | |
| 1329 DCHECK(local_state); | |
| 1330 // Test if file-selection dialogs are forbidden by policy. | |
| 1331 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs)) | |
| 1332 return false; | |
| 1333 | |
| 1334 const GURL& url = params_.src_url; | |
| 1335 bool can_save = | |
| 1336 (params_.media_flags & WebContextMenuData::MediaCanSave) && | |
| 1337 url.is_valid() && ProfileIOData::IsHandledProtocol(url.scheme()); | |
| 1338 #if defined(ENABLE_FULL_PRINTING) | |
| 1339 // Do not save the preview PDF on the print preview page. | |
| 1340 can_save = can_save && | |
| 1341 !(printing::PrintPreviewDialogController::IsPrintPreviewURL(url)); | |
| 1342 #endif | |
| 1343 return can_save; | |
| 1344 } | |
| 1345 | |
| 1346 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB: | |
| 1347 return true; | |
| 1348 | |
| 1349 case IDC_SAVE_PAGE: { | |
| 1350 CoreTabHelper* core_tab_helper = | |
| 1351 CoreTabHelper::FromWebContents(source_web_contents_); | |
| 1352 if (!core_tab_helper) | |
| 1353 return false; | |
| 1354 | |
| 1355 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate(); | |
| 1356 if (core_delegate && | |
| 1357 !core_delegate->CanSaveContents(source_web_contents_)) | |
| 1358 return false; | |
| 1359 | |
| 1360 PrefService* local_state = g_browser_process->local_state(); | |
| 1361 DCHECK(local_state); | |
| 1362 // Test if file-selection dialogs are forbidden by policy. | |
| 1363 if (!local_state->GetBoolean(prefs::kAllowFileSelectionDialogs)) | |
| 1364 return false; | |
| 1365 | |
| 1366 // Instead of using GetURL here, we use url() (which is the "real" url of | |
| 1367 // the page) from the NavigationEntry because its reflects their origin | |
| 1368 // rather than the display one (returned by GetURL) which may be | |
| 1369 // different (like having "view-source:" on the front). | |
| 1370 // TODO(nasko): Audit all GetActiveEntry calls in this file. | |
| 1371 NavigationEntry* active_entry = | |
| 1372 source_web_contents_->GetController().GetActiveEntry(); | |
| 1373 return content::IsSavableURL( | |
| 1374 (active_entry) ? active_entry->GetURL() : GURL()); | |
| 1375 } | |
| 1376 | |
| 1377 case IDC_CONTENT_CONTEXT_RELOADFRAME: | |
| 1378 return params_.frame_url.is_valid(); | |
| 1379 | |
| 1380 case IDC_CONTENT_CONTEXT_UNDO: | |
| 1381 return !!(params_.edit_flags & WebContextMenuData::CanUndo); | |
| 1382 | |
| 1383 case IDC_CONTENT_CONTEXT_REDO: | |
| 1384 return !!(params_.edit_flags & WebContextMenuData::CanRedo); | |
| 1385 | |
| 1386 case IDC_CONTENT_CONTEXT_CUT: | |
| 1387 return !!(params_.edit_flags & WebContextMenuData::CanCut); | |
| 1388 | |
| 1389 case IDC_CONTENT_CONTEXT_COPY: | |
| 1390 return !!(params_.edit_flags & WebContextMenuData::CanCopy); | |
| 1391 | |
| 1392 case IDC_CONTENT_CONTEXT_PASTE: | |
| 1393 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE: | |
| 1394 return !!(params_.edit_flags & WebContextMenuData::CanPaste); | |
| 1395 | |
| 1396 case IDC_CONTENT_CONTEXT_DELETE: | |
| 1397 return !!(params_.edit_flags & WebContextMenuData::CanDelete); | |
| 1398 | |
| 1399 case IDC_CONTENT_CONTEXT_SELECTALL: | |
| 1400 return !!(params_.edit_flags & WebContextMenuData::CanSelectAll); | |
| 1401 | |
| 1402 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD: | |
| 1403 return !profile_->IsOffTheRecord() && params_.link_url.is_valid() && | |
| 1404 incognito_avail != IncognitoModePrefs::DISABLED; | |
| 1405 | |
| 1406 case IDC_PRINT: | |
| 1407 return profile_->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) && | |
| 1408 (params_.media_type == WebContextMenuData::MediaTypeNone || | |
| 1409 params_.media_flags & WebContextMenuData::MediaCanPrint); | |
| 1410 | |
| 1411 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR: | |
| 1412 case IDC_CONTENT_CONTEXT_GOTOURL: | |
| 1413 case IDC_SPELLPANEL_TOGGLE: | |
| 1414 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS: | |
| 1415 return true; | |
| 1416 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO: | |
| 1417 // Disabled if no browser is associated (e.g. desktop notifications). | |
| 1418 if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL) | |
| 1419 return false; | |
| 1420 return true; | |
| 1421 | |
| 1422 case IDC_CHECK_SPELLING_WHILE_TYPING: | |
| 1423 return profile_->GetPrefs()->GetBoolean( | |
| 1424 prefs::kEnableContinuousSpellcheck); | |
| 1425 | |
| 1426 #if !defined(OS_MACOSX) && defined(OS_POSIX) | |
| 1427 // TODO(suzhe): this should not be enabled for password fields. | |
| 1428 case IDC_INPUT_METHODS_MENU: | |
| 1429 return true; | |
| 1430 #endif | |
| 1431 | |
| 1432 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE: | |
| 1433 return !params_.keyword_url.is_empty(); | |
| 1434 | |
| 1435 case IDC_SPELLCHECK_MENU: | |
| 1436 return true; | |
| 1437 | |
| 1438 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES: | |
| 1439 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT: | |
| 1440 case IDC_SPEECH_INPUT_MENU: | |
| 1441 return true; | |
| 1442 | |
| 1443 case IDC_CONTENT_CONTEXT_OPENLINKWITH: | |
| 1444 return true; | |
| 1445 | |
| 1446 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS: | |
| 1447 return true; | |
| 1448 | |
| 1449 default: | |
| 1450 NOTREACHED(); | |
| 1451 return false; | |
| 1452 } | |
| 1453 } | |
| 1454 | |
| 1455 bool RenderViewContextMenu::IsCommandIdChecked(int id) const { | |
| 1456 // If this command is is added by one of our observers, we dispatch it to the | |
| 1457 // observer. | |
| 1458 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); | |
| 1459 RenderViewContextMenuObserver* observer; | |
| 1460 while ((observer = it.GetNext()) != NULL) { | |
| 1461 if (observer->IsCommandIdSupported(id)) | |
| 1462 return observer->IsCommandIdChecked(id); | |
| 1463 } | |
| 1464 | |
| 1465 // See if the video is set to looping. | |
| 1466 if (id == IDC_CONTENT_CONTEXT_LOOP) { | |
| 1467 return (params_.media_flags & | |
| 1468 WebContextMenuData::MediaLoop) != 0; | |
| 1469 } | |
| 1470 | |
| 1471 if (id == IDC_CONTENT_CONTEXT_CONTROLS) { | |
| 1472 return (params_.media_flags & | |
| 1473 WebContextMenuData::MediaControls) != 0; | |
| 1474 } | |
| 1475 | |
| 1476 // Custom items. | |
| 1477 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && | |
| 1478 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { | |
| 1479 return IsCustomItemChecked(params_.custom_items, id); | |
| 1480 } | |
| 1481 | |
| 1482 // Extension items. | |
| 1483 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && | |
| 1484 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { | |
| 1485 return extension_items_.IsCommandIdChecked(id); | |
| 1486 } | |
| 1487 | |
| 1488 #if defined(ENABLE_INPUT_SPEECH) | |
| 1489 // Check box for menu item 'Block offensive words'. | |
| 1490 if (id == IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES) { | |
| 1491 return profile_->GetPrefs()->GetBoolean( | |
| 1492 prefs::kSpeechRecognitionFilterProfanities); | |
| 1493 } | |
| 1494 #endif | |
| 1495 | |
| 1496 return false; | |
| 1497 } | |
| 1498 | |
| 1499 void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) { | |
| 1500 command_executed_ = true; | |
| 1501 // If this command is is added by one of our observers, we dispatch it to the | |
| 1502 // observer. | |
| 1503 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); | |
| 1504 RenderViewContextMenuObserver* observer; | |
| 1505 while ((observer = it.GetNext()) != NULL) { | |
| 1506 if (observer->IsCommandIdSupported(id)) | |
| 1507 return observer->ExecuteCommand(id); | |
| 1508 } | |
| 1509 | |
| 1510 RecordUsedItem(id); | |
| 1511 | |
| 1512 RenderViewHost* rvh = source_web_contents_->GetRenderViewHost(); | |
| 1513 | |
| 1514 // Process custom actions range. | |
| 1515 if (id >= IDC_CONTENT_CONTEXT_CUSTOM_FIRST && | |
| 1516 id <= IDC_CONTENT_CONTEXT_CUSTOM_LAST) { | |
| 1517 unsigned action = id - IDC_CONTENT_CONTEXT_CUSTOM_FIRST; | |
| 1518 const content::CustomContextMenuContext& context = params_.custom_context; | |
| 1519 #if defined(ENABLE_PLUGINS) | |
| 1520 if (context.request_id && !context.is_pepper_menu) { | |
| 1521 ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins( | |
| 1522 source_web_contents_, false, std::string()); | |
| 1523 } | |
| 1524 #endif | |
| 1525 RenderFrameHost* render_frame_host = | |
| 1526 RenderFrameHost::FromID(render_process_id_, render_frame_id_); | |
| 1527 if (render_frame_host) | |
| 1528 render_frame_host->ExecuteCustomContextMenuCommand(action, context); | |
| 1529 return; | |
| 1530 } | |
| 1531 | |
| 1532 // Process extension menu items. | |
| 1533 if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && | |
| 1534 id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { | |
| 1535 extension_items_.ExecuteCommand(id, source_web_contents_, params_); | |
| 1536 return; | |
| 1537 } | |
| 1538 | |
| 1539 if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST && | |
| 1540 id <= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_LAST) { | |
| 1541 ProtocolHandlerRegistry::ProtocolHandlerList handlers = | |
| 1542 GetHandlersForLinkUrl(); | |
| 1543 if (handlers.empty()) { | |
| 1544 return; | |
| 1545 } | |
| 1546 content::RecordAction( | |
| 1547 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Open")); | |
| 1548 int handlerIndex = id - IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST; | |
| 1549 WindowOpenDisposition disposition = | |
| 1550 ForceNewTabDispositionFromEventFlags(event_flags); | |
| 1551 OpenURL( | |
| 1552 handlers[handlerIndex].TranslateUrl(params_.link_url), | |
| 1553 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url, | |
| 1554 params_.frame_id, | |
| 1555 disposition, | |
| 1556 content::PAGE_TRANSITION_LINK); | |
| 1557 return; | |
| 1558 } | |
| 1559 | |
| 1560 switch (id) { | |
| 1561 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: { | |
| 1562 Browser* browser = | |
| 1563 chrome::FindBrowserWithWebContents(source_web_contents_); | |
| 1564 OpenURL( | |
| 1565 params_.link_url, | |
| 1566 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url, | |
| 1567 params_.frame_id, | |
| 1568 !browser || browser->is_app() ? | |
| 1569 NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB, | |
| 1570 content::PAGE_TRANSITION_LINK); | |
| 1571 break; | |
| 1572 } | |
| 1573 case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW: | |
| 1574 OpenURL( | |
| 1575 params_.link_url, | |
| 1576 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url, | |
| 1577 params_.frame_id, | |
| 1578 NEW_WINDOW, content::PAGE_TRANSITION_LINK); | |
| 1579 break; | |
| 1580 | |
| 1581 case IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD: | |
| 1582 OpenURL(params_.link_url, | |
| 1583 GURL(), | |
| 1584 params_.frame_id, | |
| 1585 OFF_THE_RECORD, | |
| 1586 content::PAGE_TRANSITION_LINK); | |
| 1587 break; | |
| 1588 | |
| 1589 case IDC_CONTENT_CONTEXT_SAVELINKAS: { | |
| 1590 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU); | |
| 1591 const GURL& referrer = | |
| 1592 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url; | |
| 1593 const GURL& url = params_.link_url; | |
| 1594 DownloadManager* dlm = BrowserContext::GetDownloadManager(profile_); | |
| 1595 scoped_ptr<DownloadUrlParameters> dl_params( | |
| 1596 DownloadUrlParameters::FromWebContents(source_web_contents_, url)); | |
| 1597 dl_params->set_referrer( | |
| 1598 content::Referrer(referrer, params_.referrer_policy)); | |
| 1599 dl_params->set_referrer_encoding(params_.frame_charset); | |
| 1600 dl_params->set_prompt(true); | |
| 1601 dlm->DownloadUrl(dl_params.Pass()); | |
| 1602 break; | |
| 1603 } | |
| 1604 | |
| 1605 case IDC_CONTENT_CONTEXT_SAVEAVAS: | |
| 1606 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: { | |
| 1607 RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU); | |
| 1608 const GURL& referrer = | |
| 1609 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url; | |
| 1610 const GURL& url = params_.src_url; | |
| 1611 int64 post_id = -1; | |
| 1612 if (url == source_web_contents_->GetURL()) { | |
| 1613 const NavigationEntry* entry = | |
| 1614 source_web_contents_->GetController().GetActiveEntry(); | |
| 1615 if (entry) | |
| 1616 post_id = entry->GetPostID(); | |
| 1617 } | |
| 1618 DownloadManager* dlm = BrowserContext::GetDownloadManager(profile_); | |
| 1619 scoped_ptr<DownloadUrlParameters> dl_params( | |
| 1620 DownloadUrlParameters::FromWebContents(source_web_contents_, url)); | |
| 1621 dl_params->set_referrer( | |
| 1622 content::Referrer(referrer, params_.referrer_policy)); | |
| 1623 dl_params->set_post_id(post_id); | |
| 1624 dl_params->set_prefer_cache(true); | |
| 1625 if (post_id >= 0) | |
| 1626 dl_params->set_method("POST"); | |
| 1627 dl_params->set_prompt(true); | |
| 1628 dlm->DownloadUrl(dl_params.Pass()); | |
| 1629 break; | |
| 1630 } | |
| 1631 | |
| 1632 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION: | |
| 1633 WriteURLToClipboard(params_.unfiltered_link_url); | |
| 1634 break; | |
| 1635 | |
| 1636 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION: | |
| 1637 case IDC_CONTENT_CONTEXT_COPYAVLOCATION: | |
| 1638 WriteURLToClipboard(params_.src_url); | |
| 1639 break; | |
| 1640 | |
| 1641 case IDC_CONTENT_CONTEXT_COPYIMAGE: | |
| 1642 CopyImageAt(params_.x, params_.y); | |
| 1643 break; | |
| 1644 | |
| 1645 case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE: | |
| 1646 GetImageThumbnailForSearch(); | |
| 1647 break; | |
| 1648 | |
| 1649 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB: | |
| 1650 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB: | |
| 1651 OpenURL( | |
| 1652 params_.src_url, | |
| 1653 params_.frame_url.is_empty() ? params_.page_url : params_.frame_url, | |
| 1654 params_.frame_id, | |
| 1655 NEW_BACKGROUND_TAB, content::PAGE_TRANSITION_LINK); | |
| 1656 break; | |
| 1657 | |
| 1658 case IDC_CONTENT_CONTEXT_PLAYPAUSE: { | |
| 1659 bool play = !!(params_.media_flags & WebContextMenuData::MediaPaused); | |
| 1660 if (play) { | |
| 1661 content::RecordAction(UserMetricsAction("MediaContextMenu_Play")); | |
| 1662 } else { | |
| 1663 content::RecordAction(UserMetricsAction("MediaContextMenu_Pause")); | |
| 1664 } | |
| 1665 MediaPlayerActionAt(gfx::Point(params_.x, params_.y), | |
| 1666 WebMediaPlayerAction( | |
| 1667 WebMediaPlayerAction::Play, play)); | |
| 1668 break; | |
| 1669 } | |
| 1670 | |
| 1671 case IDC_CONTENT_CONTEXT_MUTE: { | |
| 1672 bool mute = !(params_.media_flags & WebContextMenuData::MediaMuted); | |
| 1673 if (mute) { | |
| 1674 content::RecordAction(UserMetricsAction("MediaContextMenu_Mute")); | |
| 1675 } else { | |
| 1676 content::RecordAction(UserMetricsAction("MediaContextMenu_Unmute")); | |
| 1677 } | |
| 1678 MediaPlayerActionAt(gfx::Point(params_.x, params_.y), | |
| 1679 WebMediaPlayerAction( | |
| 1680 WebMediaPlayerAction::Mute, mute)); | |
| 1681 break; | |
| 1682 } | |
| 1683 | |
| 1684 case IDC_CONTENT_CONTEXT_LOOP: | |
| 1685 content::RecordAction(UserMetricsAction("MediaContextMenu_Loop")); | |
| 1686 MediaPlayerActionAt(gfx::Point(params_.x, params_.y), | |
| 1687 WebMediaPlayerAction( | |
| 1688 WebMediaPlayerAction::Loop, | |
| 1689 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP))); | |
| 1690 break; | |
| 1691 | |
| 1692 case IDC_CONTENT_CONTEXT_CONTROLS: | |
| 1693 content::RecordAction(UserMetricsAction("MediaContextMenu_Controls")); | |
| 1694 MediaPlayerActionAt( | |
| 1695 gfx::Point(params_.x, params_.y), | |
| 1696 WebMediaPlayerAction( | |
| 1697 WebMediaPlayerAction::Controls, | |
| 1698 !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS))); | |
| 1699 break; | |
| 1700 | |
| 1701 case IDC_CONTENT_CONTEXT_ROTATECW: | |
| 1702 content::RecordAction( | |
| 1703 UserMetricsAction("PluginContextMenu_RotateClockwise")); | |
| 1704 PluginActionAt( | |
| 1705 gfx::Point(params_.x, params_.y), | |
| 1706 WebPluginAction( | |
| 1707 WebPluginAction::Rotate90Clockwise, | |
| 1708 true)); | |
| 1709 break; | |
| 1710 | |
| 1711 case IDC_CONTENT_CONTEXT_ROTATECCW: | |
| 1712 content::RecordAction( | |
| 1713 UserMetricsAction("PluginContextMenu_RotateCounterclockwise")); | |
| 1714 PluginActionAt( | |
| 1715 gfx::Point(params_.x, params_.y), | |
| 1716 WebPluginAction( | |
| 1717 WebPluginAction::Rotate90Counterclockwise, | |
| 1718 true)); | |
| 1719 break; | |
| 1720 | |
| 1721 case IDC_BACK: | |
| 1722 source_web_contents_->GetController().GoBack(); | |
| 1723 break; | |
| 1724 | |
| 1725 case IDC_FORWARD: | |
| 1726 source_web_contents_->GetController().GoForward(); | |
| 1727 break; | |
| 1728 | |
| 1729 case IDC_SAVE_PAGE: | |
| 1730 source_web_contents_->OnSavePage(); | |
| 1731 break; | |
| 1732 | |
| 1733 case IDC_RELOAD: | |
| 1734 source_web_contents_->GetController().Reload(true); | |
| 1735 break; | |
| 1736 | |
| 1737 case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP: { | |
| 1738 const Extension* platform_app = GetExtension(); | |
| 1739 DCHECK(platform_app); | |
| 1740 DCHECK(platform_app->is_platform_app()); | |
| 1741 | |
| 1742 extensions::ExtensionSystem::Get(profile_)->extension_service()-> | |
| 1743 ReloadExtension(platform_app->id()); | |
| 1744 break; | |
| 1745 } | |
| 1746 | |
| 1747 case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP: { | |
| 1748 const Extension* platform_app = GetExtension(); | |
| 1749 DCHECK(platform_app); | |
| 1750 DCHECK(platform_app->is_platform_app()); | |
| 1751 | |
| 1752 apps::AppLoadService::Get(profile_)->RestartApplication( | |
| 1753 platform_app->id()); | |
| 1754 break; | |
| 1755 } | |
| 1756 | |
| 1757 case IDC_PRINT: | |
| 1758 #if defined(ENABLE_PRINTING) | |
| 1759 if (params_.media_type == WebContextMenuData::MediaTypeNone) { | |
| 1760 #if defined(ENABLE_FULL_PRINTING) | |
| 1761 printing::PrintViewManager* print_view_manager = | |
| 1762 printing::PrintViewManager::FromWebContents(source_web_contents_); | |
| 1763 | |
| 1764 if (!print_view_manager) | |
| 1765 break; | |
| 1766 if (profile_->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled)) { | |
| 1767 print_view_manager->PrintNow(); | |
| 1768 } else { | |
| 1769 print_view_manager->PrintPreviewNow(!params_.selection_text.empty()); | |
| 1770 } | |
| 1771 #else | |
| 1772 printing::PrintViewManagerBasic* print_view_manager = | |
| 1773 printing::PrintViewManagerBasic::FromWebContents( | |
| 1774 source_web_contents_); | |
| 1775 if (!print_view_manager) | |
| 1776 break; | |
| 1777 print_view_manager->PrintNow(); | |
| 1778 #endif // defined(ENABLE_FULL_PRINTING) | |
| 1779 } else { | |
| 1780 rvh->Send(new PrintMsg_PrintNodeUnderContextMenu(rvh->GetRoutingID())); | |
| 1781 } | |
| 1782 #endif // defined(ENABLE_PRINTING) | |
| 1783 break; | |
| 1784 | |
| 1785 case IDC_VIEW_SOURCE: | |
| 1786 source_web_contents_->ViewSource(); | |
| 1787 break; | |
| 1788 | |
| 1789 case IDC_CONTENT_CONTEXT_INSPECTELEMENT: | |
| 1790 Inspect(params_.x, params_.y); | |
| 1791 break; | |
| 1792 | |
| 1793 case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE: { | |
| 1794 const Extension* platform_app = GetExtension(); | |
| 1795 DCHECK(platform_app); | |
| 1796 DCHECK(platform_app->is_platform_app()); | |
| 1797 | |
| 1798 extensions::devtools_util::InspectBackgroundPage(platform_app, profile_); | |
| 1799 break; | |
| 1800 } | |
| 1801 | |
| 1802 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: { | |
| 1803 NavigationController* controller = &source_web_contents_->GetController(); | |
| 1804 // Important to use GetVisibleEntry to match what's showing in the | |
| 1805 // omnibox. | |
| 1806 NavigationEntry* nav_entry = controller->GetVisibleEntry(); | |
| 1807 Browser* browser = | |
| 1808 chrome::FindBrowserWithWebContents(source_web_contents_); | |
| 1809 chrome::ShowWebsiteSettings(browser, source_web_contents_, | |
| 1810 nav_entry->GetURL(), nav_entry->GetSSL()); | |
| 1811 break; | |
| 1812 } | |
| 1813 | |
| 1814 case IDC_CONTENT_CONTEXT_TRANSLATE: { | |
| 1815 // A translation might have been triggered by the time the menu got | |
| 1816 // selected, do nothing in that case. | |
| 1817 TranslateTabHelper* translate_tab_helper = | |
| 1818 TranslateTabHelper::FromWebContents(source_web_contents_); | |
| 1819 if (!translate_tab_helper || | |
| 1820 translate_tab_helper->GetLanguageState().IsPageTranslated() || | |
| 1821 translate_tab_helper->GetLanguageState().translation_pending()) { | |
| 1822 return; | |
| 1823 } | |
| 1824 std::string original_lang = | |
| 1825 translate_tab_helper->GetLanguageState().original_language(); | |
| 1826 std::string target_lang = g_browser_process->GetApplicationLocale(); | |
| 1827 target_lang = TranslateDownloadManager::GetLanguageCode(target_lang); | |
| 1828 // Since the user decided to translate for that language and site, clears | |
| 1829 // any preferences for not translating them. | |
| 1830 scoped_ptr<TranslatePrefs> prefs( | |
| 1831 TranslateTabHelper::CreateTranslatePrefs(profile_->GetPrefs())); | |
| 1832 prefs->UnblockLanguage(original_lang); | |
| 1833 prefs->RemoveSiteFromBlacklist(params_.page_url.HostNoBrackets()); | |
| 1834 TranslateManager* manager = translate_tab_helper->GetTranslateManager(); | |
| 1835 DCHECK(manager); | |
| 1836 manager->TranslatePage(original_lang, target_lang); | |
| 1837 break; | |
| 1838 } | |
| 1839 | |
| 1840 case IDC_CONTENT_CONTEXT_RELOADFRAME: | |
| 1841 rvh->ReloadFrame(); | |
| 1842 break; | |
| 1843 | |
| 1844 case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE: | |
| 1845 source_web_contents_->ViewFrameSource(params_.frame_url, | |
| 1846 params_.frame_page_state); | |
| 1847 break; | |
| 1848 | |
| 1849 case IDC_CONTENT_CONTEXT_VIEWFRAMEINFO: { | |
| 1850 Browser* browser = chrome::FindBrowserWithWebContents( | |
| 1851 source_web_contents_); | |
| 1852 chrome::ShowWebsiteSettings(browser, source_web_contents_, | |
| 1853 params_.frame_url, params_.security_info); | |
| 1854 break; | |
| 1855 } | |
| 1856 | |
| 1857 case IDC_CONTENT_CONTEXT_UNDO: | |
| 1858 rvh->Undo(); | |
| 1859 break; | |
| 1860 | |
| 1861 case IDC_CONTENT_CONTEXT_REDO: | |
| 1862 rvh->Redo(); | |
| 1863 break; | |
| 1864 | |
| 1865 case IDC_CONTENT_CONTEXT_CUT: | |
| 1866 rvh->Cut(); | |
| 1867 break; | |
| 1868 | |
| 1869 case IDC_CONTENT_CONTEXT_COPY: | |
| 1870 rvh->Copy(); | |
| 1871 break; | |
| 1872 | |
| 1873 case IDC_CONTENT_CONTEXT_PASTE: | |
| 1874 rvh->Paste(); | |
| 1875 break; | |
| 1876 | |
| 1877 case IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE: | |
| 1878 rvh->PasteAndMatchStyle(); | |
| 1879 break; | |
| 1880 | |
| 1881 case IDC_CONTENT_CONTEXT_DELETE: | |
| 1882 rvh->Delete(); | |
| 1883 break; | |
| 1884 | |
| 1885 case IDC_CONTENT_CONTEXT_SELECTALL: | |
| 1886 rvh->SelectAll(); | |
| 1887 break; | |
| 1888 | |
| 1889 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR: | |
| 1890 case IDC_CONTENT_CONTEXT_GOTOURL: { | |
| 1891 WindowOpenDisposition disposition = | |
| 1892 ForceNewTabDispositionFromEventFlags(event_flags); | |
| 1893 OpenURL(selection_navigation_url_, | |
| 1894 GURL(), | |
| 1895 params_.frame_id, | |
| 1896 disposition, | |
| 1897 content::PAGE_TRANSITION_LINK); | |
| 1898 break; | |
| 1899 } | |
| 1900 case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS: { | |
| 1901 WindowOpenDisposition disposition = | |
| 1902 ForceNewTabDispositionFromEventFlags(event_flags); | |
| 1903 std::string url = std::string(chrome::kChromeUISettingsURL) + | |
| 1904 chrome::kLanguageOptionsSubPage; | |
| 1905 OpenURL(GURL(url), GURL(), 0, disposition, content::PAGE_TRANSITION_LINK); | |
| 1906 break; | |
| 1907 } | |
| 1908 | |
| 1909 case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS: { | |
| 1910 content::RecordAction( | |
| 1911 UserMetricsAction("RegisterProtocolHandler.ContextMenu_Settings")); | |
| 1912 WindowOpenDisposition disposition = | |
| 1913 ForceNewTabDispositionFromEventFlags(event_flags); | |
| 1914 std::string url = std::string(chrome::kChromeUISettingsURL) + | |
| 1915 chrome::kHandlerSettingsSubPage; | |
| 1916 OpenURL(GURL(url), GURL(), 0, disposition, content::PAGE_TRANSITION_LINK); | |
| 1917 break; | |
| 1918 } | |
| 1919 | |
| 1920 case IDC_CONTENT_CONTEXT_ADDSEARCHENGINE: { | |
| 1921 // Make sure the model is loaded. | |
| 1922 TemplateURLService* model = | |
| 1923 TemplateURLServiceFactory::GetForProfile(profile_); | |
| 1924 if (!model) | |
| 1925 return; | |
| 1926 model->Load(); | |
| 1927 | |
| 1928 SearchEngineTabHelper* search_engine_tab_helper = | |
| 1929 SearchEngineTabHelper::FromWebContents(source_web_contents_); | |
| 1930 if (search_engine_tab_helper && | |
| 1931 search_engine_tab_helper->delegate()) { | |
| 1932 base::string16 keyword( | |
| 1933 TemplateURLService::GenerateKeyword(params_.page_url)); | |
| 1934 TemplateURLData data; | |
| 1935 data.short_name = keyword; | |
| 1936 data.SetKeyword(keyword); | |
| 1937 data.SetURL(params_.keyword_url.spec()); | |
| 1938 data.favicon_url = | |
| 1939 TemplateURL::GenerateFaviconURL(params_.page_url.GetOrigin()); | |
| 1940 // Takes ownership of the TemplateURL. | |
| 1941 search_engine_tab_helper->delegate()-> | |
| 1942 ConfirmAddSearchProvider(new TemplateURL(profile_, data), profile_); | |
| 1943 } | |
| 1944 break; | |
| 1945 } | |
| 1946 | |
| 1947 #if defined(ENABLE_INPUT_SPEECH) | |
| 1948 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_FILTER_PROFANITIES: { | |
| 1949 profile_->GetPrefs()->SetBoolean( | |
| 1950 prefs::kSpeechRecognitionFilterProfanities, | |
| 1951 !profile_->GetPrefs()->GetBoolean( | |
| 1952 prefs::kSpeechRecognitionFilterProfanities)); | |
| 1953 break; | |
| 1954 } | |
| 1955 #endif | |
| 1956 case IDC_CONTENT_CONTEXT_SPEECH_INPUT_ABOUT: { | |
| 1957 GURL url(chrome::kSpeechInputAboutURL); | |
| 1958 GURL localized_url = google_util::AppendGoogleLocaleParam(url); | |
| 1959 // Open URL with no referrer field (because user clicked on menu item). | |
| 1960 OpenURL(localized_url, GURL(), 0, NEW_FOREGROUND_TAB, | |
| 1961 content::PAGE_TRANSITION_LINK); | |
| 1962 break; | |
| 1963 } | |
| 1964 | |
| 1965 default: | |
| 1966 NOTREACHED(); | |
| 1967 break; | |
| 1968 } | |
| 1969 } | |
| 1970 | |
| 1971 ProtocolHandlerRegistry::ProtocolHandlerList | |
| 1972 RenderViewContextMenu::GetHandlersForLinkUrl() { | |
| 1973 ProtocolHandlerRegistry::ProtocolHandlerList handlers = | |
| 1974 protocol_handler_registry_->GetHandlersFor(params_.link_url.scheme()); | |
| 1975 std::sort(handlers.begin(), handlers.end()); | |
| 1976 return handlers; | |
| 1977 } | |
| 1978 | |
| 1979 void RenderViewContextMenu::MenuWillShow(ui::SimpleMenuModel* source) { | |
| 1980 for (int i = 0; i < source->GetItemCount(); ++i) { | |
| 1981 if (source->IsVisibleAt(i) && | |
| 1982 source->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR) { | |
| 1983 RecordShownItem(source->GetCommandIdAt(i)); | |
| 1984 } | |
| 1985 } | |
| 1986 | |
| 1987 // Ignore notifications from submenus. | |
| 1988 if (source != &menu_model_) | |
| 1989 return; | |
| 1990 | |
| 1991 content::RenderWidgetHostView* view = | |
| 1992 source_web_contents_->GetRenderWidgetHostView(); | |
| 1993 if (view) | |
| 1994 view->SetShowingContextMenu(true); | |
| 1995 | |
| 1996 content::NotificationService::current()->Notify( | |
| 1997 chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN, | |
| 1998 content::Source<RenderViewContextMenu>(this), | |
| 1999 content::NotificationService::NoDetails()); | |
| 2000 } | |
| 2001 | |
| 2002 void RenderViewContextMenu::MenuClosed(ui::SimpleMenuModel* source) { | |
| 2003 // Ignore notifications from submenus. | |
| 2004 if (source != &menu_model_) | |
| 2005 return; | |
| 2006 | |
| 2007 content::RenderWidgetHostView* view = | |
| 2008 source_web_contents_->GetRenderWidgetHostView(); | |
| 2009 if (view) | |
| 2010 view->SetShowingContextMenu(false); | |
| 2011 RenderFrameHost* render_frame_host = | |
| 2012 RenderFrameHost::FromID(render_process_id_, render_frame_id_); | |
| 2013 if (render_frame_host) | |
| 2014 render_frame_host->NotifyContextMenuClosed(params_.custom_context); | |
| 2015 | |
| 2016 if (!command_executed_) { | |
| 2017 FOR_EACH_OBSERVER(RenderViewContextMenuObserver, | |
| 2018 observers_, | |
| 2019 OnMenuCancel()); | |
| 2020 } | |
| 2021 } | |
| 2022 | |
| 2023 bool RenderViewContextMenu::IsDevCommandEnabled(int id) const { | |
| 2024 if (id == IDC_CONTENT_CONTEXT_INSPECTELEMENT || | |
| 2025 id == IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE) { | |
| 2026 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
| 2027 if (!profile_->GetPrefs()->GetBoolean(prefs::kWebKitJavascriptEnabled) || | |
| 2028 command_line->HasSwitch(switches::kDisableJavaScript)) | |
| 2029 return false; | |
| 2030 | |
| 2031 // Don't enable the web inspector if the developer tools are disabled via | |
| 2032 // the preference dev-tools-disabled. | |
| 2033 if (profile_->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled)) | |
| 2034 return false; | |
| 2035 } | |
| 2036 | |
| 2037 return true; | |
| 2038 } | |
| 2039 | |
| 2040 base::string16 RenderViewContextMenu::PrintableSelectionText() { | |
| 2041 return gfx::TruncateString(params_.selection_text, | |
| 2042 kMaxSelectionTextLength); | |
| 2043 } | |
| 2044 | |
| 2045 // Controller functions -------------------------------------------------------- | |
| 2046 | |
| 2047 void RenderViewContextMenu::OpenURL( | |
| 2048 const GURL& url, const GURL& referrer, int64 frame_id, | |
| 2049 WindowOpenDisposition disposition, | |
| 2050 content::PageTransition transition) { | |
| 2051 // Ensure that URL fragment, username and password fields are not sent | |
| 2052 // in the referrer. | |
| 2053 GURL sanitized_referrer(referrer); | |
| 2054 if (sanitized_referrer.is_valid() && (sanitized_referrer.has_ref() || | |
| 2055 sanitized_referrer.has_username() || sanitized_referrer.has_password())) { | |
| 2056 GURL::Replacements referrer_mods; | |
| 2057 referrer_mods.ClearRef(); | |
| 2058 referrer_mods.ClearUsername(); | |
| 2059 referrer_mods.ClearPassword(); | |
| 2060 sanitized_referrer = sanitized_referrer.ReplaceComponents(referrer_mods); | |
| 2061 } | |
| 2062 | |
| 2063 WebContents* new_contents = source_web_contents_->OpenURL(OpenURLParams( | |
| 2064 url, content::Referrer(sanitized_referrer, params_.referrer_policy), | |
| 2065 disposition, transition, false)); | |
| 2066 if (!new_contents) | |
| 2067 return; | |
| 2068 | |
| 2069 RetargetingDetails details; | |
| 2070 details.source_web_contents = source_web_contents_; | |
| 2071 details.source_frame_id = frame_id; | |
| 2072 details.target_url = url; | |
| 2073 details.target_web_contents = new_contents; | |
| 2074 details.not_yet_in_tabstrip = false; | |
| 2075 content::NotificationService::current()->Notify( | |
| 2076 chrome::NOTIFICATION_RETARGETING, | |
| 2077 content::Source<Profile>(Profile::FromBrowserContext( | |
| 2078 source_web_contents_->GetBrowserContext())), | |
| 2079 content::Details<RetargetingDetails>(&details)); | |
| 2080 } | |
| 2081 | |
| 2082 void RenderViewContextMenu::CopyImageAt(int x, int y) { | |
| 2083 source_web_contents_->GetRenderViewHost()->CopyImageAt(x, y); | |
| 2084 } | |
| 2085 | |
| 2086 void RenderViewContextMenu::GetImageThumbnailForSearch() { | |
| 2087 source_web_contents_->GetRenderViewHost()->Send( | |
| 2088 new ChromeViewMsg_RequestThumbnailForContextNode( | |
| 2089 source_web_contents_->GetRenderViewHost()->GetRoutingID(), | |
| 2090 kImageSearchThumbnailMinSize, | |
| 2091 gfx::Size(kImageSearchThumbnailMaxWidth, | |
| 2092 kImageSearchThumbnailMaxHeight))); | |
| 2093 } | |
| 2094 | |
| 2095 void RenderViewContextMenu::Inspect(int x, int y) { | |
| 2096 content::RecordAction(UserMetricsAction("DevTools_InspectElement")); | |
| 2097 source_web_contents_->GetRenderViewHostAtPosition( | |
| 2098 x, y, base::Bind(&DevToolsInspectElementAt)); | |
| 2099 } | |
| 2100 | |
| 2101 void RenderViewContextMenu::WriteURLToClipboard(const GURL& url) { | |
| 2102 chrome_common_net::WriteURLToClipboard( | |
| 2103 url, | |
| 2104 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), | |
| 2105 ui::Clipboard::GetForCurrentThread()); | |
| 2106 } | |
| 2107 | |
| 2108 void RenderViewContextMenu::MediaPlayerActionAt( | |
| 2109 const gfx::Point& location, | |
| 2110 const WebMediaPlayerAction& action) { | |
| 2111 source_web_contents_->GetRenderViewHost()-> | |
| 2112 ExecuteMediaPlayerActionAtLocation(location, action); | |
| 2113 } | |
| 2114 | |
| 2115 void RenderViewContextMenu::PluginActionAt( | |
| 2116 const gfx::Point& location, | |
| 2117 const WebPluginAction& action) { | |
| 2118 source_web_contents_->GetRenderViewHost()-> | |
| 2119 ExecutePluginActionAtLocation(location, action); | |
| 2120 } | |
| OLD | NEW |