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_danger_prompt.h"
6
7#include "base/bind.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/browser/chrome_notification_types.h"
10#include "chrome/browser/download/chrome_download_manager_delegate.h"
11#include "chrome/browser/download/download_stats.h"
12#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
13#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
14#include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
15#include "chrome/grit/chromium_strings.h"
16#include "chrome/grit/generated_resources.h"
17#include "content/public/browser/download_danger_type.h"
18#include "content/public/browser/download_item.h"
19#include "ui/base/l10n/l10n_util.h"
20
21using extensions::ExperienceSamplingEvent;
22
23namespace {
24
25// TODO(wittman): Create a native web contents modal dialog implementation of
26// this dialog for non-Views platforms, to support bold formatting of the
27// message lead.
28
29// Implements DownloadDangerPrompt using a TabModalConfirmDialog.
30class DownloadDangerPromptImpl : public DownloadDangerPrompt,
31                                 public content::DownloadItem::Observer,
32                                 public TabModalConfirmDialogDelegate {
33 public:
34  DownloadDangerPromptImpl(content::DownloadItem* item,
35                           content::WebContents* web_contents,
36                           bool show_context,
37                           const OnDone& done);
38  virtual ~DownloadDangerPromptImpl();
39
40  // DownloadDangerPrompt:
41  virtual void InvokeActionForTesting(Action action) OVERRIDE;
42
43 private:
44  // content::DownloadItem::Observer:
45  virtual void OnDownloadUpdated(content::DownloadItem* download) OVERRIDE;
46
47  // TabModalConfirmDialogDelegate:
48  virtual base::string16 GetTitle() OVERRIDE;
49  virtual base::string16 GetDialogMessage() OVERRIDE;
50  virtual base::string16 GetAcceptButtonTitle() OVERRIDE;
51  virtual base::string16 GetCancelButtonTitle() OVERRIDE;
52  virtual void OnAccepted() OVERRIDE;
53  virtual void OnCanceled() OVERRIDE;
54  virtual void OnClosed() OVERRIDE;
55
56  void RunDone(Action action);
57
58  content::DownloadItem* download_;
59  bool show_context_;
60  OnDone done_;
61
62  scoped_ptr<ExperienceSamplingEvent> sampling_event_;
63
64  DISALLOW_COPY_AND_ASSIGN(DownloadDangerPromptImpl);
65};
66
67DownloadDangerPromptImpl::DownloadDangerPromptImpl(
68    content::DownloadItem* download,
69    content::WebContents* web_contents,
70    bool show_context,
71    const OnDone& done)
72    : TabModalConfirmDialogDelegate(web_contents),
73      download_(download),
74      show_context_(show_context),
75      done_(done) {
76  DCHECK(!done_.is_null());
77  download_->AddObserver(this);
78  RecordOpenedDangerousConfirmDialog(download_->GetDangerType());
79
80  // ExperienceSampling: A malicious download warning is being shown to the
81  // user, so we start a new SamplingEvent and track it.
82  sampling_event_.reset(new ExperienceSamplingEvent(
83      ExperienceSamplingEvent::kDownloadDangerPrompt,
84      download->GetURL(),
85      download->GetReferrerUrl(),
86      download->GetBrowserContext()));
87}
88
89DownloadDangerPromptImpl::~DownloadDangerPromptImpl() {
90  // |this| might be deleted without invoking any callbacks. E.g. pressing Esc
91  // on GTK or if the user navigates away from the page showing the prompt.
92  RunDone(DISMISS);
93}
94
95void DownloadDangerPromptImpl::InvokeActionForTesting(Action action) {
96  switch (action) {
97    case ACCEPT: Accept(); break;
98    case CANCEL: Cancel(); break;
99    case DISMISS:
100      RunDone(DISMISS);
101      Cancel();
102      break;
103  }
104}
105
106void DownloadDangerPromptImpl::OnDownloadUpdated(
107    content::DownloadItem* download) {
108  // If the download is nolonger dangerous (accepted externally) or the download
109  // is in a terminal state, then the download danger prompt is no longer
110  // necessary.
111  if (!download->IsDangerous() || download->IsDone()) {
112    RunDone(DISMISS);
113    Cancel();
114  }
115}
116
117base::string16 DownloadDangerPromptImpl::GetTitle() {
118  if (show_context_)
119    return l10n_util::GetStringUTF16(IDS_CONFIRM_KEEP_DANGEROUS_DOWNLOAD_TITLE);
120  switch (download_->GetDangerType()) {
121    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
122    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
123    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
124    case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
125      return l10n_util::GetStringUTF16(
126          IDS_RESTORE_KEEP_DANGEROUS_DOWNLOAD_TITLE);
127    }
128    default: {
129      return l10n_util::GetStringUTF16(
130          IDS_CONFIRM_KEEP_DANGEROUS_DOWNLOAD_TITLE);
131    }
132  }
133}
134
135base::string16 DownloadDangerPromptImpl::GetDialogMessage() {
136  if (show_context_) {
137    switch (download_->GetDangerType()) {
138      case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: {
139        return l10n_util::GetStringFUTF16(
140            IDS_PROMPT_DANGEROUS_DOWNLOAD,
141            download_->GetFileNameToReportUser().LossyDisplayName());
142      }
143      case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: // Fall through
144      case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
145      case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
146        return l10n_util::GetStringFUTF16(
147            IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT,
148            download_->GetFileNameToReportUser().LossyDisplayName());
149      }
150      case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
151        return l10n_util::GetStringFUTF16(
152            IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT,
153            download_->GetFileNameToReportUser().LossyDisplayName());
154      }
155      case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
156        return l10n_util::GetStringFUTF16(
157            IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS,
158            download_->GetFileNameToReportUser().LossyDisplayName());
159      }
160      case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
161      case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
162      case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
163      case content::DOWNLOAD_DANGER_TYPE_MAX: {
164        break;
165      }
166    }
167  } else {
168    switch (download_->GetDangerType()) {
169      case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
170      case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
171      case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: {
172        return l10n_util::GetStringUTF16(
173            IDS_PROMPT_CONFIRM_KEEP_MALICIOUS_DOWNLOAD_LEAD) +
174            base::ASCIIToUTF16("\n\n") +
175            l10n_util::GetStringUTF16(
176                IDS_PROMPT_CONFIRM_KEEP_MALICIOUS_DOWNLOAD_BODY);
177      }
178      default: {
179        return l10n_util::GetStringUTF16(
180            IDS_PROMPT_CONFIRM_KEEP_DANGEROUS_DOWNLOAD);
181      }
182    }
183  }
184  NOTREACHED();
185  return base::string16();
186}
187
188base::string16 DownloadDangerPromptImpl::GetAcceptButtonTitle() {
189  if (show_context_)
190    return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD);
191  switch (download_->GetDangerType()) {
192    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
193    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
194    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
195    case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
196      return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD_AGAIN_MALICIOUS);
197    }
198    default:
199      return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD_AGAIN);
200  }
201}
202
203base::string16 DownloadDangerPromptImpl::GetCancelButtonTitle() {
204  if (show_context_)
205    return l10n_util::GetStringUTF16(IDS_CANCEL);
206  switch (download_->GetDangerType()) {
207    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
208    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
209    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
210    case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: {
211      return l10n_util::GetStringUTF16(IDS_CONFIRM_CANCEL_AGAIN_MALICIOUS);
212    }
213    default:
214      return l10n_util::GetStringUTF16(IDS_CANCEL);
215  }
216}
217
218void DownloadDangerPromptImpl::OnAccepted() {
219  // ExperienceSampling: User proceeded through the warning.
220  sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kProceed);
221  RunDone(ACCEPT);
222}
223
224void DownloadDangerPromptImpl::OnCanceled() {
225  // ExperienceSampling: User canceled the warning.
226  sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny);
227  RunDone(CANCEL);
228}
229
230void DownloadDangerPromptImpl::OnClosed() {
231  // ExperienceSampling: User canceled the warning.
232  sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny);
233  RunDone(DISMISS);
234}
235
236void DownloadDangerPromptImpl::RunDone(Action action) {
237  // Invoking the callback can cause the download item state to change or cause
238  // the constrained window to close, and |callback| refers to a member
239  // variable.
240  OnDone done = done_;
241  done_.Reset();
242  if (download_ != NULL) {
243    download_->RemoveObserver(this);
244    download_ = NULL;
245  }
246  if (!done.is_null())
247    done.Run(action);
248}
249
250}  // namespace
251
252#if !defined(USE_AURA)
253// static
254DownloadDangerPrompt* DownloadDangerPrompt::Create(
255    content::DownloadItem* item,
256    content::WebContents* web_contents,
257    bool show_context,
258    const OnDone& done) {
259  DownloadDangerPromptImpl* prompt = new DownloadDangerPromptImpl(
260      item, web_contents, show_context, done);
261  // |prompt| will be deleted when the dialog is done.
262  TabModalConfirmDialog::Create(prompt, web_contents);
263  return prompt;
264}
265#endif
266