download_danger_prompt_views.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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
24namespace {
25
26const int kMessageWidth = 320;
27const 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.
32class 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
75DownloadDangerPromptViews::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:
123void 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:
141base::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
155base::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
162void DownloadDangerPromptViews::DeleteDelegate() {
163  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
164  delete this;
165}
166
167ui::ModalType DownloadDangerPromptViews::GetModalType() const {
168  return ui::MODAL_TYPE_CHILD;
169}
170
171bool DownloadDangerPromptViews::Cancel() {
172  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
173  RunDone(CANCEL);
174  return true;
175}
176
177bool DownloadDangerPromptViews::Accept() {
178  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
179  RunDone(ACCEPT);
180  return true;
181}
182
183bool DownloadDangerPromptViews::Close() {
184  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
185  RunDone(DISMISS);
186  return true;
187}
188
189views::View* DownloadDangerPromptViews::GetInitiallyFocusedView() {
190  return GetDialogClientView()->cancel_button();
191}
192
193views::View* DownloadDangerPromptViews::GetContentsView() {
194  return contents_view_;
195}
196
197views::Widget* DownloadDangerPromptViews::GetWidget() {
198  return contents_view_->GetWidget();
199}
200
201const views::Widget* DownloadDangerPromptViews::GetWidget() const {
202  return contents_view_->GetWidget();
203}
204
205// content::DownloadItem::Observer:
206void 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
217base::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
231base::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
245base::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
262base::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
312void 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
327DownloadDangerPrompt* 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