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