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