1 // Copyright (c) 2012 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/download/download_shelf_context_menu.h" 6 7 #include "base/command_line.h" 8 #include "chrome/browser/browser_process.h" 9 #include "chrome/browser/download/download_crx_util.h" 10 #include "chrome/browser/download/download_item_model.h" 11 #include "chrome/browser/download/download_prefs.h" 12 #include "chrome/browser/safe_browsing/download_feedback_service.h" 13 #include "chrome/browser/safe_browsing/download_protection_service.h" 14 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 15 #include "chrome/common/url_constants.h" 16 #include "content/public/browser/download_item.h" 17 #include "content/public/browser/download_manager.h" 18 #include "content/public/browser/page_navigator.h" 19 #include "content/public/common/content_switches.h" 20 #include "extensions/common/extension.h" 21 #include "grit/generated_resources.h" 22 #include "ui/base/l10n/l10n_util.h" 23 24 using content::DownloadItem; 25 using extensions::Extension; 26 27 namespace { 28 29 // Returns true if downloads resumption is enabled. 30 bool IsDownloadResumptionEnabled() { 31 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 32 return command_line.HasSwitch(switches::kEnableDownloadResumption); 33 } 34 35 } // namespace 36 37 DownloadShelfContextMenu::~DownloadShelfContextMenu() { 38 DetachFromDownloadItem(); 39 } 40 41 DownloadShelfContextMenu::DownloadShelfContextMenu( 42 DownloadItem* download_item, 43 content::PageNavigator* navigator) 44 : download_item_(download_item), 45 navigator_(navigator) { 46 DCHECK(download_item_); 47 download_item_->AddObserver(this); 48 } 49 50 ui::SimpleMenuModel* DownloadShelfContextMenu::GetMenuModel() { 51 ui::SimpleMenuModel* model = NULL; 52 53 if (!download_item_) 54 return NULL; 55 56 DownloadItemModel download_model(download_item_); 57 // We shouldn't be opening a context menu for a dangerous download, unless it 58 // is a malicious download. 59 DCHECK(!download_model.IsDangerous() || download_model.MightBeMalicious()); 60 61 if (download_model.IsMalicious()) 62 model = GetMaliciousMenuModel(); 63 else if (download_model.MightBeMalicious()) 64 model = GetMaybeMaliciousMenuModel(); 65 else if (download_item_->GetState() == DownloadItem::COMPLETE) 66 model = GetFinishedMenuModel(); 67 else if (download_item_->GetState() == DownloadItem::INTERRUPTED) 68 model = GetInterruptedMenuModel(); 69 else 70 model = GetInProgressMenuModel(); 71 return model; 72 } 73 74 bool DownloadShelfContextMenu::IsCommandIdEnabled(int command_id) const { 75 if (!download_item_) 76 return false; 77 78 switch (static_cast<ContextMenuCommands>(command_id)) { 79 case SHOW_IN_FOLDER: 80 return download_item_->CanShowInFolder(); 81 case OPEN_WHEN_COMPLETE: 82 case PLATFORM_OPEN: 83 return download_item_->CanOpenDownload() && 84 !download_crx_util::IsExtensionDownload(*download_item_); 85 case ALWAYS_OPEN_TYPE: 86 // For temporary downloads, the target filename might be a temporary 87 // filename. Don't base an "Always open" decision based on it. Also 88 // exclude extensions. 89 return download_item_->CanOpenDownload() && 90 !download_crx_util::IsExtensionDownload(*download_item_); 91 case CANCEL: 92 return !download_item_->IsDone(); 93 case TOGGLE_PAUSE: 94 return !download_item_->IsDone(); 95 case DISCARD: 96 case KEEP: 97 case REPORT: 98 case LEARN_MORE_SCANNING: 99 case LEARN_MORE_INTERRUPTED: 100 return true; 101 } 102 return false; 103 } 104 105 bool DownloadShelfContextMenu::IsCommandIdChecked(int command_id) const { 106 if (!download_item_) 107 return false; 108 109 switch (command_id) { 110 case OPEN_WHEN_COMPLETE: 111 return download_item_->GetOpenWhenComplete() || 112 download_crx_util::IsExtensionDownload(*download_item_); 113 case ALWAYS_OPEN_TYPE: 114 return download_item_->ShouldOpenFileBasedOnExtension(); 115 case TOGGLE_PAUSE: 116 return download_item_->IsPaused(); 117 } 118 return false; 119 } 120 121 void DownloadShelfContextMenu::ExecuteCommand(int command_id, int event_flags) { 122 if (!download_item_) 123 return; 124 125 switch (static_cast<ContextMenuCommands>(command_id)) { 126 case SHOW_IN_FOLDER: 127 download_item_->ShowDownloadInShell(); 128 break; 129 case OPEN_WHEN_COMPLETE: 130 download_item_->OpenDownload(); 131 break; 132 case ALWAYS_OPEN_TYPE: { 133 DownloadPrefs* prefs = DownloadPrefs::FromBrowserContext( 134 download_item_->GetBrowserContext()); 135 base::FilePath path = download_item_->GetTargetFilePath(); 136 if (!IsCommandIdChecked(ALWAYS_OPEN_TYPE)) 137 prefs->EnableAutoOpenBasedOnExtension(path); 138 else 139 prefs->DisableAutoOpenBasedOnExtension(path); 140 break; 141 } 142 case PLATFORM_OPEN: 143 DownloadItemModel(download_item_).OpenUsingPlatformHandler(); 144 break; 145 case CANCEL: 146 download_item_->Cancel(true /* Cancelled by user */); 147 break; 148 case TOGGLE_PAUSE: 149 if (download_item_->GetState() == DownloadItem::IN_PROGRESS && 150 !download_item_->IsPaused()) { 151 download_item_->Pause(); 152 } else { 153 download_item_->Resume(); 154 } 155 break; 156 case DISCARD: 157 download_item_->Remove(); 158 break; 159 case KEEP: 160 download_item_->ValidateDangerousDownload(); 161 break; 162 case REPORT: { 163 #if defined(FULL_SAFE_BROWSING) 164 using safe_browsing::DownloadProtectionService; 165 DownloadItemModel download_model(download_item_); 166 if (!download_model.ShouldAllowDownloadFeedback()) 167 break; 168 SafeBrowsingService* sb_service = 169 g_browser_process->safe_browsing_service(); 170 DownloadProtectionService* protection_service = 171 (sb_service ? sb_service->download_protection_service() : NULL); 172 if (protection_service) { 173 protection_service->feedback_service()->BeginFeedbackForDownload( 174 download_item_); 175 } 176 #else 177 // Should only be getting invoked if we are using safe browsing. 178 NOTREACHED(); 179 #endif 180 break; 181 } 182 case LEARN_MORE_SCANNING: { 183 #if defined(FULL_SAFE_BROWSING) 184 using safe_browsing::DownloadProtectionService; 185 SafeBrowsingService* sb_service = 186 g_browser_process->safe_browsing_service(); 187 DownloadProtectionService* protection_service = 188 (sb_service ? sb_service->download_protection_service() : NULL); 189 if (protection_service) { 190 protection_service->ShowDetailsForDownload(*download_item_, navigator_); 191 } 192 #else 193 // Should only be getting invoked if we are using safe browsing. 194 NOTREACHED(); 195 #endif 196 break; 197 } 198 case LEARN_MORE_INTERRUPTED: 199 navigator_->OpenURL( 200 content::OpenURLParams(GURL(chrome::kDownloadInterruptedLearnMoreURL), 201 content::Referrer(), 202 NEW_FOREGROUND_TAB, 203 content::PAGE_TRANSITION_LINK, 204 false)); 205 break; 206 } 207 } 208 209 bool DownloadShelfContextMenu::GetAcceleratorForCommandId( 210 int command_id, ui::Accelerator* accelerator) { 211 return false; 212 } 213 214 bool DownloadShelfContextMenu::IsItemForCommandIdDynamic(int command_id) const { 215 return command_id == TOGGLE_PAUSE; 216 } 217 218 base::string16 DownloadShelfContextMenu::GetLabelForCommandId( 219 int command_id) const { 220 switch (static_cast<ContextMenuCommands>(command_id)) { 221 case SHOW_IN_FOLDER: 222 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_SHOW); 223 case OPEN_WHEN_COMPLETE: 224 if (download_item_ && !download_item_->IsDone()) 225 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_OPEN_WHEN_COMPLETE); 226 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_OPEN); 227 case ALWAYS_OPEN_TYPE: 228 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_ALWAYS_OPEN_TYPE); 229 case PLATFORM_OPEN: 230 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_PLATFORM_OPEN); 231 case CANCEL: 232 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_CANCEL); 233 case TOGGLE_PAUSE: 234 if (download_item_ && 235 download_item_->GetState() == DownloadItem::IN_PROGRESS && 236 !download_item_->IsPaused()) 237 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_PAUSE_ITEM); 238 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_RESUME_ITEM); 239 case DISCARD: 240 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_DISCARD); 241 case REPORT: 242 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_REPORT); 243 case KEEP: 244 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_KEEP); 245 case LEARN_MORE_SCANNING: 246 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); 247 case LEARN_MORE_INTERRUPTED: 248 return l10n_util::GetStringUTF16( 249 IDS_DOWNLOAD_MENU_LEARN_MORE_INTERRUPTED); 250 } 251 NOTREACHED(); 252 return base::string16(); 253 } 254 255 void DownloadShelfContextMenu::DetachFromDownloadItem() { 256 if (!download_item_) 257 return; 258 259 download_item_->RemoveObserver(this); 260 download_item_ = NULL; 261 } 262 263 void DownloadShelfContextMenu::OnDownloadDestroyed(DownloadItem* download) { 264 DCHECK(download_item_ == download); 265 DetachFromDownloadItem(); 266 } 267 268 ui::SimpleMenuModel* DownloadShelfContextMenu::GetInProgressMenuModel() { 269 if (in_progress_download_menu_model_) 270 return in_progress_download_menu_model_.get(); 271 272 in_progress_download_menu_model_.reset(new ui::SimpleMenuModel(this)); 273 274 in_progress_download_menu_model_->AddCheckItemWithStringId( 275 OPEN_WHEN_COMPLETE, IDS_DOWNLOAD_MENU_OPEN_WHEN_COMPLETE); 276 in_progress_download_menu_model_->AddCheckItemWithStringId( 277 ALWAYS_OPEN_TYPE, IDS_DOWNLOAD_MENU_ALWAYS_OPEN_TYPE); 278 in_progress_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 279 in_progress_download_menu_model_->AddItemWithStringId( 280 TOGGLE_PAUSE, IDS_DOWNLOAD_MENU_PAUSE_ITEM); 281 in_progress_download_menu_model_->AddItemWithStringId( 282 SHOW_IN_FOLDER, IDS_DOWNLOAD_MENU_SHOW); 283 in_progress_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 284 in_progress_download_menu_model_->AddItemWithStringId( 285 CANCEL, IDS_DOWNLOAD_MENU_CANCEL); 286 287 return in_progress_download_menu_model_.get(); 288 } 289 290 ui::SimpleMenuModel* DownloadShelfContextMenu::GetFinishedMenuModel() { 291 if (finished_download_menu_model_) 292 return finished_download_menu_model_.get(); 293 294 finished_download_menu_model_.reset(new ui::SimpleMenuModel(this)); 295 296 finished_download_menu_model_->AddItemWithStringId( 297 OPEN_WHEN_COMPLETE, IDS_DOWNLOAD_MENU_OPEN); 298 finished_download_menu_model_->AddCheckItemWithStringId( 299 ALWAYS_OPEN_TYPE, IDS_DOWNLOAD_MENU_ALWAYS_OPEN_TYPE); 300 if (DownloadItemModel(download_item_).ShouldPreferOpeningInBrowser()) 301 finished_download_menu_model_->AddItemWithStringId( 302 PLATFORM_OPEN, IDS_DOWNLOAD_MENU_PLATFORM_OPEN); 303 finished_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 304 finished_download_menu_model_->AddItemWithStringId( 305 SHOW_IN_FOLDER, IDS_DOWNLOAD_MENU_SHOW); 306 finished_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 307 finished_download_menu_model_->AddItemWithStringId( 308 CANCEL, IDS_DOWNLOAD_MENU_CANCEL); 309 310 return finished_download_menu_model_.get(); 311 } 312 313 ui::SimpleMenuModel* DownloadShelfContextMenu::GetInterruptedMenuModel() { 314 #if !defined(OS_WIN) 315 // If resumption isn't enabled and we aren't on Windows, then none of the 316 // options here are applicable. 317 if (!IsDownloadResumptionEnabled()) 318 return GetInProgressMenuModel(); 319 #endif 320 321 if (interrupted_download_menu_model_) 322 return interrupted_download_menu_model_.get(); 323 324 interrupted_download_menu_model_.reset(new ui::SimpleMenuModel(this)); 325 326 if (IsDownloadResumptionEnabled()) { 327 interrupted_download_menu_model_->AddItemWithStringId( 328 TOGGLE_PAUSE, IDS_DOWNLOAD_MENU_RESUME_ITEM); 329 } 330 #if defined(OS_WIN) 331 // The Help Center article is currently Windows specific. 332 // TODO(asanka): Enable this for other platforms when the article is expanded 333 // for other platforms. 334 interrupted_download_menu_model_->AddItemWithStringId( 335 LEARN_MORE_INTERRUPTED, IDS_DOWNLOAD_MENU_LEARN_MORE_INTERRUPTED); 336 #endif 337 if (IsDownloadResumptionEnabled()) { 338 interrupted_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 339 interrupted_download_menu_model_->AddItemWithStringId( 340 CANCEL, IDS_DOWNLOAD_MENU_CANCEL); 341 } 342 343 return interrupted_download_menu_model_.get(); 344 } 345 346 ui::SimpleMenuModel* DownloadShelfContextMenu::GetMaybeMaliciousMenuModel() { 347 if (maybe_malicious_download_menu_model_) 348 return maybe_malicious_download_menu_model_.get(); 349 350 maybe_malicious_download_menu_model_.reset(new ui::SimpleMenuModel(this)); 351 352 maybe_malicious_download_menu_model_->AddItemWithStringId( 353 DISCARD, IDS_DOWNLOAD_MENU_DISCARD); 354 maybe_malicious_download_menu_model_->AddItemWithStringId( 355 KEEP, IDS_DOWNLOAD_MENU_KEEP); 356 maybe_malicious_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 357 maybe_malicious_download_menu_model_->AddItemWithStringId( 358 LEARN_MORE_SCANNING, IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); 359 return maybe_malicious_download_menu_model_.get(); 360 } 361 362 ui::SimpleMenuModel* 363 DownloadShelfContextMenu::GetMaliciousMenuModel() { 364 if (malicious_download_menu_model_) 365 return malicious_download_menu_model_.get(); 366 367 malicious_download_menu_model_.reset(new ui::SimpleMenuModel(this)); 368 369 DownloadItemModel download_model(download_item_); 370 if (download_model.ShouldAllowDownloadFeedback()) { 371 malicious_download_menu_model_->AddItemWithStringId( 372 REPORT, IDS_DOWNLOAD_MENU_REPORT); 373 } 374 malicious_download_menu_model_->AddItemWithStringId( 375 LEARN_MORE_SCANNING, IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); 376 377 return malicious_download_menu_model_.get(); 378 } 379