Home | History | Annotate | Download | only in download
      1 // Copyright 2013 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 "base/compiler_specific.h"
      6 #include "chrome/browser/download/download_danger_prompt.h"
      7 #include "chrome/browser/download/download_stats.h"
      8 #include "chrome/browser/ui/views/constrained_window_views.h"
      9 #include "content/public/browser/browser_thread.h"
     10 #include "content/public/browser/download_danger_type.h"
     11 #include "content/public/browser/download_item.h"
     12 #include "grit/chromium_strings.h"
     13 #include "grit/generated_resources.h"
     14 #include "ui/base/l10n/l10n_util.h"
     15 #include "ui/base/resource/resource_bundle.h"
     16 #include "ui/views/controls/button/label_button.h"
     17 #include "ui/views/controls/label.h"
     18 #include "ui/views/layout/grid_layout.h"
     19 #include "ui/views/view.h"
     20 #include "ui/views/widget/widget.h"
     21 #include "ui/views/window/dialog_client_view.h"
     22 #include "ui/views/window/dialog_delegate.h"
     23 
     24 namespace {
     25 
     26 const int kMessageWidth = 320;
     27 const int kParagraphPadding = 15;
     28 
     29 // Views-specific implementation of download danger prompt dialog. We use this
     30 // class rather than a TabModalConfirmDialog so that we can use custom
     31 // formatting on the text in the body of the dialog.
     32 class DownloadDangerPromptViews : public DownloadDangerPrompt,
     33                                   public content::DownloadItem::Observer,
     34                                   public views::DialogDelegate {
     35  public:
     36   DownloadDangerPromptViews(content::DownloadItem* item,
     37                             bool show_context,
     38                             const OnDone& done);
     39 
     40   // DownloadDangerPrompt methods:
     41   virtual void InvokeActionForTesting(Action action) OVERRIDE;
     42 
     43   // views::DialogDelegate methods:
     44   virtual base::string16 GetDialogButtonLabel(
     45       ui::DialogButton button) const OVERRIDE;
     46   virtual base::string16 GetWindowTitle() const OVERRIDE;
     47   virtual void DeleteDelegate() OVERRIDE;
     48   virtual ui::ModalType GetModalType() const OVERRIDE;
     49   virtual bool Cancel() OVERRIDE;
     50   virtual bool Accept() OVERRIDE;
     51   virtual bool Close() OVERRIDE;
     52   virtual views::View* GetInitiallyFocusedView() OVERRIDE;
     53   virtual views::View* GetContentsView() OVERRIDE;
     54   virtual views::Widget* GetWidget() OVERRIDE;
     55   virtual const views::Widget* GetWidget() const OVERRIDE;
     56 
     57   // content::DownloadItem::Observer:
     58   virtual void OnDownloadUpdated(content::DownloadItem* download) OVERRIDE;
     59 
     60  private:
     61   base::string16 GetAcceptButtonTitle() const;
     62   base::string16 GetCancelButtonTitle() const;
     63   // The message lead is separated from the main text and is bolded.
     64   base::string16 GetMessageLead() const;
     65   base::string16 GetMessageBody() const;
     66   void RunDone(Action action);
     67 
     68   content::DownloadItem* download_;
     69   bool show_context_;
     70   OnDone done_;
     71 
     72   views::View* contents_view_;
     73 };
     74 
     75 DownloadDangerPromptViews::DownloadDangerPromptViews(
     76     content::DownloadItem* item,
     77     bool show_context,
     78     const OnDone& done)
     79     : download_(item),
     80       show_context_(show_context),
     81       done_(done),
     82       contents_view_(NULL) {
     83   DCHECK(!done_.is_null());
     84   download_->AddObserver(this);
     85 
     86   contents_view_ = new views::View;
     87 
     88   views::GridLayout* layout = views::GridLayout::CreatePanel(contents_view_);
     89   contents_view_->SetLayoutManager(layout);
     90 
     91   views::ColumnSet* column_set = layout->AddColumnSet(0);
     92   column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
     93                         views::GridLayout::FIXED, kMessageWidth, 0);
     94 
     95   const base::string16 message_lead = GetMessageLead();
     96 
     97   if (!message_lead.empty()) {
     98     ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
     99     views::Label* message_lead_label = new views::Label(
    100         message_lead, rb->GetFontList(ui::ResourceBundle::BoldFont));
    101     message_lead_label->SetMultiLine(true);
    102     message_lead_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    103     message_lead_label->SetAllowCharacterBreak(true);
    104 
    105     layout->StartRow(0, 0);
    106     layout->AddView(message_lead_label);
    107 
    108     layout->AddPaddingRow(0, kParagraphPadding);
    109   }
    110 
    111   views::Label* message_body_label = new views::Label(GetMessageBody());
    112   message_body_label->SetMultiLine(true);
    113   message_body_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    114   message_body_label->SetAllowCharacterBreak(true);
    115 
    116   layout->StartRow(0, 0);
    117   layout->AddView(message_body_label);
    118 
    119   RecordOpenedDangerousConfirmDialog(download_->GetDangerType());
    120 }
    121 
    122 // DownloadDangerPrompt methods:
    123 void DownloadDangerPromptViews::InvokeActionForTesting(Action action) {
    124   switch (action) {
    125     case ACCEPT:
    126       Accept();
    127       break;
    128 
    129     case CANCEL:
    130     case DISMISS:
    131       Cancel();
    132       break;
    133 
    134     default:
    135       NOTREACHED();
    136       break;
    137   }
    138 }
    139 
    140 // views::DialogDelegate methods:
    141 base::string16 DownloadDangerPromptViews::GetDialogButtonLabel(
    142     ui::DialogButton button) const {
    143   switch (button) {
    144     case ui::DIALOG_BUTTON_OK:
    145       return GetAcceptButtonTitle();
    146 
    147     case ui::DIALOG_BUTTON_CANCEL:
    148       return GetCancelButtonTitle();
    149 
    150     default:
    151       return DialogDelegate::GetDialogButtonLabel(button);
    152   }
    153 }
    154 
    155 base::string16 DownloadDangerPromptViews::GetWindowTitle() const {
    156   if (show_context_)
    157     return l10n_util::GetStringUTF16(IDS_CONFIRM_KEEP_DANGEROUS_DOWNLOAD_TITLE);
    158   else
    159     return l10n_util::GetStringUTF16(IDS_RESTORE_KEEP_DANGEROUS_DOWNLOAD_TITLE);
    160 }
    161 
    162 void DownloadDangerPromptViews::DeleteDelegate() {
    163   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    164   delete this;
    165 }
    166 
    167 ui::ModalType DownloadDangerPromptViews::GetModalType() const {
    168   return ui::MODAL_TYPE_CHILD;
    169 }
    170 
    171 bool DownloadDangerPromptViews::Cancel() {
    172   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    173   RunDone(CANCEL);
    174   return true;
    175 }
    176 
    177 bool DownloadDangerPromptViews::Accept() {
    178   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    179   RunDone(ACCEPT);
    180   return true;
    181 }
    182 
    183 bool DownloadDangerPromptViews::Close() {
    184   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    185   RunDone(DISMISS);
    186   return true;
    187 }
    188 
    189 views::View* DownloadDangerPromptViews::GetInitiallyFocusedView() {
    190   return GetDialogClientView()->cancel_button();
    191 }
    192 
    193 views::View* DownloadDangerPromptViews::GetContentsView() {
    194   return contents_view_;
    195 }
    196 
    197 views::Widget* DownloadDangerPromptViews::GetWidget() {
    198   return contents_view_->GetWidget();
    199 }
    200 
    201 const views::Widget* DownloadDangerPromptViews::GetWidget() const {
    202   return contents_view_->GetWidget();
    203 }
    204 
    205 // content::DownloadItem::Observer:
    206 void DownloadDangerPromptViews::OnDownloadUpdated(
    207     content::DownloadItem* download) {
    208   // If the download is nolonger dangerous (accepted externally) or the download
    209   // is in a terminal state, then the download danger prompt is no longer
    210   // necessary.
    211   if (!download_->IsDangerous() || download_->IsDone()) {
    212     RunDone(DISMISS);
    213     Cancel();
    214   }
    215 }
    216 
    217 base::string16 DownloadDangerPromptViews::GetAcceptButtonTitle() const {
    218   if (show_context_)
    219     return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD);
    220   switch (download_->GetDangerType()) {
    221     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
    222     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
    223     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
    224       return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD_AGAIN_MALICIOUS);
    225     }
    226     default:
    227       return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD_AGAIN);
    228   }
    229 }
    230 
    231 base::string16 DownloadDangerPromptViews::GetCancelButtonTitle() const {
    232   if (show_context_)
    233     return l10n_util::GetStringUTF16(IDS_CANCEL);
    234   switch (download_->GetDangerType()) {
    235     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
    236     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
    237     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
    238       return l10n_util::GetStringUTF16(IDS_CONFIRM_CANCEL_AGAIN_MALICIOUS);
    239     }
    240     default:
    241       return l10n_util::GetStringUTF16(IDS_CANCEL);
    242   }
    243 }
    244 
    245 base::string16 DownloadDangerPromptViews::GetMessageLead() const {
    246   if (!show_context_) {
    247     switch (download_->GetDangerType()) {
    248       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
    249       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
    250       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
    251         return l10n_util::GetStringUTF16(
    252             IDS_PROMPT_CONFIRM_KEEP_MALICIOUS_DOWNLOAD_LEAD);
    253 
    254       default:
    255         break;
    256     }
    257   }
    258 
    259   return base::string16();
    260 }
    261 
    262 base::string16 DownloadDangerPromptViews::GetMessageBody() const {
    263   if (show_context_) {
    264     switch (download_->GetDangerType()) {
    265       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: {
    266         return l10n_util::GetStringFUTF16(
    267             IDS_PROMPT_DANGEROUS_DOWNLOAD,
    268             download_->GetFileNameToReportUser().LossyDisplayName());
    269       }
    270       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:  // Fall through
    271       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
    272       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
    273         return l10n_util::GetStringFUTF16(
    274             IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
    275             download_->GetFileNameToReportUser().LossyDisplayName());
    276       }
    277       case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
    278         return l10n_util::GetStringFUTF16(
    279             IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT,
    280             download_->GetFileNameToReportUser().LossyDisplayName());
    281       }
    282       case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
    283         return l10n_util::GetStringFUTF16(
    284             IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS,
    285             download_->GetFileNameToReportUser().LossyDisplayName());
    286       }
    287       case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
    288       case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
    289       case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
    290       case content::DOWNLOAD_DANGER_TYPE_MAX: {
    291         break;
    292       }
    293     }
    294   } else {
    295     switch (download_->GetDangerType()) {
    296       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
    297       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
    298       case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
    299         return l10n_util::GetStringUTF16(
    300             IDS_PROMPT_CONFIRM_KEEP_MALICIOUS_DOWNLOAD_BODY);
    301       }
    302       default: {
    303         return l10n_util::GetStringUTF16(
    304             IDS_PROMPT_CONFIRM_KEEP_DANGEROUS_DOWNLOAD);
    305       }
    306     }
    307   }
    308   NOTREACHED();
    309   return base::string16();
    310 }
    311 
    312 void DownloadDangerPromptViews::RunDone(Action action) {
    313   // Invoking the callback can cause the download item state to change or cause
    314   // the window to close, and |callback| refers to a member variable.
    315   OnDone done = done_;
    316   done_.Reset();
    317   if (download_ != NULL) {
    318     download_->RemoveObserver(this);
    319     download_ = NULL;
    320   }
    321   if (!done.is_null())
    322     done.Run(action);
    323 }
    324 
    325 }  // namespace
    326 
    327 DownloadDangerPrompt* DownloadDangerPrompt::Create(
    328     content::DownloadItem* item,
    329     content::WebContents* web_contents,
    330     bool show_context,
    331     const OnDone& done) {
    332   DownloadDangerPromptViews* download_danger_prompt =
    333       new DownloadDangerPromptViews(item, show_context, done);
    334   ShowWebModalDialogViews(download_danger_prompt, web_contents);
    335   return download_danger_prompt;
    336 }
    337