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/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