Home | History | Annotate | Download | only in download
      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