// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/download/download_shelf_context_menu.h" #include "base/command_line.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_crx_util.h" #include "chrome/browser/download/download_item_model.h" #include "chrome/browser/download/download_prefs.h" #include "chrome/browser/download/download_target_determiner.h" #include "chrome/browser/safe_browsing/download_protection_service.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/common/url_constants.h" #include "content/public/browser/download_item.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/page_navigator.h" #include "content/public/common/content_switches.h" #include "extensions/common/extension.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #if defined(OS_WIN) #include "chrome/browser/ui/pdf/adobe_reader_info_win.h" #endif using content::DownloadItem; namespace { // Returns true if downloads resumption is enabled. bool IsDownloadResumptionEnabled() { return base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableDownloadResumption); } } // namespace DownloadShelfContextMenu::~DownloadShelfContextMenu() { DetachFromDownloadItem(); } DownloadShelfContextMenu::DownloadShelfContextMenu( DownloadItem* download_item, content::PageNavigator* navigator) : download_item_(download_item), navigator_(navigator) { DCHECK(download_item_); download_item_->AddObserver(this); #if defined(OS_WIN) is_pdf_reader_up_to_date_ = false; if (IsDownloadPdf() && IsAdobeReaderDefaultPDFViewer()) { is_pdf_reader_up_to_date_ = DownloadTargetDeterminer::IsAdobeReaderUpToDate(); } #endif // defined(OS_WIN) } ui::SimpleMenuModel* DownloadShelfContextMenu::GetMenuModel() { ui::SimpleMenuModel* model = NULL; if (!download_item_) return NULL; DownloadItemModel download_model(download_item_); // We shouldn't be opening a context menu for a dangerous download, unless it // is a malicious download. DCHECK(!download_model.IsDangerous() || download_model.MightBeMalicious()); if (download_model.IsMalicious()) model = GetMaliciousMenuModel(); else if (download_model.MightBeMalicious()) model = GetMaybeMaliciousMenuModel(); else if (download_item_->GetState() == DownloadItem::COMPLETE) model = GetFinishedMenuModel(); else if (download_item_->GetState() == DownloadItem::INTERRUPTED) model = GetInterruptedMenuModel(); else model = GetInProgressMenuModel(); return model; } bool DownloadShelfContextMenu::IsCommandIdEnabled(int command_id) const { if (!download_item_) return false; switch (static_cast(command_id)) { case SHOW_IN_FOLDER: return download_item_->CanShowInFolder(); case OPEN_WHEN_COMPLETE: case PLATFORM_OPEN: return download_item_->CanOpenDownload() && !download_crx_util::IsExtensionDownload(*download_item_); case ALWAYS_OPEN_TYPE: // For temporary downloads, the target filename might be a temporary // filename. Don't base an "Always open" decision based on it. Also // exclude extensions. return download_item_->CanOpenDownload() && !download_crx_util::IsExtensionDownload(*download_item_); case CANCEL: return !download_item_->IsDone(); case TOGGLE_PAUSE: return !download_item_->IsDone(); case DISCARD: case KEEP: case LEARN_MORE_SCANNING: case LEARN_MORE_INTERRUPTED: return true; } NOTREACHED(); return false; } bool DownloadShelfContextMenu::IsCommandIdChecked(int command_id) const { if (!download_item_) return false; switch (command_id) { case OPEN_WHEN_COMPLETE: return download_item_->GetOpenWhenComplete() || download_crx_util::IsExtensionDownload(*download_item_); case ALWAYS_OPEN_TYPE: #if defined(OS_WIN) if (CanOpenPdfInReader()) { DownloadPrefs* prefs = DownloadPrefs::FromBrowserContext( download_item_->GetBrowserContext()); return prefs->ShouldOpenPdfInAdobeReader(); } #endif return download_item_->ShouldOpenFileBasedOnExtension(); case TOGGLE_PAUSE: return download_item_->IsPaused(); } return false; } bool DownloadShelfContextMenu::IsCommandIdVisible(int command_id) const { if (!download_item_) return false; if (command_id == PLATFORM_OPEN) return (DownloadItemModel(download_item_).ShouldPreferOpeningInBrowser()); return true; } void DownloadShelfContextMenu::ExecuteCommand(int command_id, int event_flags) { if (!download_item_) return; switch (static_cast(command_id)) { case SHOW_IN_FOLDER: download_item_->ShowDownloadInShell(); break; case OPEN_WHEN_COMPLETE: download_item_->OpenDownload(); break; case ALWAYS_OPEN_TYPE: { bool is_checked = IsCommandIdChecked(ALWAYS_OPEN_TYPE); DownloadPrefs* prefs = DownloadPrefs::FromBrowserContext( download_item_->GetBrowserContext()); #if defined(OS_WIN) if (CanOpenPdfInReader()) { prefs->SetShouldOpenPdfInAdobeReader(!is_checked); DownloadItemModel(download_item_).SetShouldPreferOpeningInBrowser( is_checked); break; } #endif base::FilePath path = download_item_->GetTargetFilePath(); if (is_checked) prefs->DisableAutoOpenBasedOnExtension(path); else prefs->EnableAutoOpenBasedOnExtension(path); break; } case PLATFORM_OPEN: DownloadItemModel(download_item_).OpenUsingPlatformHandler(); break; case CANCEL: download_item_->Cancel(true /* Cancelled by user */); break; case TOGGLE_PAUSE: if (download_item_->GetState() == DownloadItem::IN_PROGRESS && !download_item_->IsPaused()) { download_item_->Pause(); } else { download_item_->Resume(); } break; case DISCARD: download_item_->Remove(); break; case KEEP: download_item_->ValidateDangerousDownload(); break; case LEARN_MORE_SCANNING: { #if defined(FULL_SAFE_BROWSING) using safe_browsing::DownloadProtectionService; SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service(); DownloadProtectionService* protection_service = (sb_service ? sb_service->download_protection_service() : NULL); if (protection_service) { protection_service->ShowDetailsForDownload(*download_item_, navigator_); } #else // Should only be getting invoked if we are using safe browsing. NOTREACHED(); #endif break; } case LEARN_MORE_INTERRUPTED: navigator_->OpenURL( content::OpenURLParams(GURL(chrome::kDownloadInterruptedLearnMoreURL), content::Referrer(), NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, false)); break; } } bool DownloadShelfContextMenu::GetAcceleratorForCommandId( int command_id, ui::Accelerator* accelerator) { return false; } bool DownloadShelfContextMenu::IsItemForCommandIdDynamic(int command_id) const { return command_id == TOGGLE_PAUSE; } base::string16 DownloadShelfContextMenu::GetLabelForCommandId( int command_id) const { switch (static_cast(command_id)) { case SHOW_IN_FOLDER: return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_SHOW); case OPEN_WHEN_COMPLETE: if (download_item_ && !download_item_->IsDone()) return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_OPEN_WHEN_COMPLETE); return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_OPEN); case ALWAYS_OPEN_TYPE: return l10n_util::GetStringUTF16(GetAlwaysOpenStringId()); case PLATFORM_OPEN: return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_PLATFORM_OPEN); case CANCEL: return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_CANCEL); case TOGGLE_PAUSE: if (download_item_ && download_item_->GetState() == DownloadItem::IN_PROGRESS && !download_item_->IsPaused()) return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_PAUSE_ITEM); return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_RESUME_ITEM); case DISCARD: return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_DISCARD); case KEEP: return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_KEEP); case LEARN_MORE_SCANNING: return l10n_util::GetStringUTF16(IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); case LEARN_MORE_INTERRUPTED: return l10n_util::GetStringUTF16( IDS_DOWNLOAD_MENU_LEARN_MORE_INTERRUPTED); } NOTREACHED(); return base::string16(); } void DownloadShelfContextMenu::DetachFromDownloadItem() { if (!download_item_) return; download_item_->RemoveObserver(this); download_item_ = NULL; } void DownloadShelfContextMenu::OnDownloadDestroyed(DownloadItem* download) { DCHECK(download_item_ == download); DetachFromDownloadItem(); } ui::SimpleMenuModel* DownloadShelfContextMenu::GetInProgressMenuModel() { if (in_progress_download_menu_model_) return in_progress_download_menu_model_.get(); in_progress_download_menu_model_.reset(new ui::SimpleMenuModel(this)); in_progress_download_menu_model_->AddCheckItemWithStringId( OPEN_WHEN_COMPLETE, IDS_DOWNLOAD_MENU_OPEN_WHEN_COMPLETE); in_progress_download_menu_model_->AddCheckItemWithStringId( ALWAYS_OPEN_TYPE, GetAlwaysOpenStringId()); in_progress_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); in_progress_download_menu_model_->AddItemWithStringId( TOGGLE_PAUSE, IDS_DOWNLOAD_MENU_PAUSE_ITEM); in_progress_download_menu_model_->AddItemWithStringId( SHOW_IN_FOLDER, IDS_DOWNLOAD_MENU_SHOW); in_progress_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); in_progress_download_menu_model_->AddItemWithStringId( CANCEL, IDS_DOWNLOAD_MENU_CANCEL); return in_progress_download_menu_model_.get(); } ui::SimpleMenuModel* DownloadShelfContextMenu::GetFinishedMenuModel() { if (finished_download_menu_model_) return finished_download_menu_model_.get(); finished_download_menu_model_.reset(new ui::SimpleMenuModel(this)); finished_download_menu_model_->AddItemWithStringId( OPEN_WHEN_COMPLETE, IDS_DOWNLOAD_MENU_OPEN); finished_download_menu_model_->AddCheckItemWithStringId( ALWAYS_OPEN_TYPE, GetAlwaysOpenStringId()); finished_download_menu_model_->AddItemWithStringId( PLATFORM_OPEN, IDS_DOWNLOAD_MENU_PLATFORM_OPEN); finished_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); finished_download_menu_model_->AddItemWithStringId( SHOW_IN_FOLDER, IDS_DOWNLOAD_MENU_SHOW); finished_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); finished_download_menu_model_->AddItemWithStringId( CANCEL, IDS_DOWNLOAD_MENU_CANCEL); return finished_download_menu_model_.get(); } ui::SimpleMenuModel* DownloadShelfContextMenu::GetInterruptedMenuModel() { #if !defined(OS_WIN) // If resumption isn't enabled and we aren't on Windows, then none of the // options here are applicable. if (!IsDownloadResumptionEnabled()) return GetInProgressMenuModel(); #endif if (interrupted_download_menu_model_) return interrupted_download_menu_model_.get(); interrupted_download_menu_model_.reset(new ui::SimpleMenuModel(this)); if (IsDownloadResumptionEnabled()) { interrupted_download_menu_model_->AddItemWithStringId( TOGGLE_PAUSE, IDS_DOWNLOAD_MENU_RESUME_ITEM); } #if defined(OS_WIN) // The Help Center article is currently Windows specific. // TODO(asanka): Enable this for other platforms when the article is expanded // for other platforms. interrupted_download_menu_model_->AddItemWithStringId( LEARN_MORE_INTERRUPTED, IDS_DOWNLOAD_MENU_LEARN_MORE_INTERRUPTED); #endif if (IsDownloadResumptionEnabled()) { interrupted_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); interrupted_download_menu_model_->AddItemWithStringId( CANCEL, IDS_DOWNLOAD_MENU_CANCEL); } return interrupted_download_menu_model_.get(); } ui::SimpleMenuModel* DownloadShelfContextMenu::GetMaybeMaliciousMenuModel() { if (maybe_malicious_download_menu_model_) return maybe_malicious_download_menu_model_.get(); maybe_malicious_download_menu_model_.reset(new ui::SimpleMenuModel(this)); maybe_malicious_download_menu_model_->AddItemWithStringId( KEEP, IDS_DOWNLOAD_MENU_KEEP); maybe_malicious_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); maybe_malicious_download_menu_model_->AddItemWithStringId( LEARN_MORE_SCANNING, IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); return maybe_malicious_download_menu_model_.get(); } ui::SimpleMenuModel* DownloadShelfContextMenu::GetMaliciousMenuModel() { if (malicious_download_menu_model_) return malicious_download_menu_model_.get(); malicious_download_menu_model_.reset(new ui::SimpleMenuModel(this)); DownloadItemModel download_model(download_item_); malicious_download_menu_model_->AddItemWithStringId( LEARN_MORE_SCANNING, IDS_DOWNLOAD_MENU_LEARN_MORE_SCANNING); return malicious_download_menu_model_.get(); } int DownloadShelfContextMenu::GetAlwaysOpenStringId() const { #if defined(OS_WIN) if (CanOpenPdfInReader()) return IDS_DOWNLOAD_MENU_ALWAYS_OPEN_PDF_IN_READER; #endif return IDS_DOWNLOAD_MENU_ALWAYS_OPEN_TYPE; } #if defined(OS_WIN) bool DownloadShelfContextMenu::IsDownloadPdf() const { base::FilePath path = download_item_->GetTargetFilePath(); return path.MatchesExtension(FILE_PATH_LITERAL(".pdf")); } bool DownloadShelfContextMenu::CanOpenPdfInReader() const { return (is_pdf_reader_up_to_date_ && IsDownloadPdf()); } #endif