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.
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/extensions/api/experience_sampling_private/experience_sampling.h"
9 #include "chrome/grit/chromium_strings.h"
10 #include "chrome/grit/generated_resources.h"
11 #include "components/constrained_window/constrained_window_views.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/download_danger_type.h"
14 #include "content/public/browser/download_item.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/views/controls/button/label_button.h"
18 #include "ui/views/controls/label.h"
19 #include "ui/views/layout/grid_layout.h"
20 #include "ui/views/view.h"
21 #include "ui/views/widget/widget.h"
22 #include "ui/views/window/dialog_client_view.h"
23 #include "ui/views/window/dialog_delegate.h"
25 using extensions::ExperienceSamplingEvent
;
29 const int kMessageWidth
= 320;
30 const int kParagraphPadding
= 15;
32 // Views-specific implementation of download danger prompt dialog. We use this
33 // class rather than a TabModalConfirmDialog so that we can use custom
34 // formatting on the text in the body of the dialog.
35 class DownloadDangerPromptViews
: public DownloadDangerPrompt
,
36 public content::DownloadItem::Observer
,
37 public views::DialogDelegate
{
39 DownloadDangerPromptViews(content::DownloadItem
* item
,
43 // DownloadDangerPrompt methods:
44 void InvokeActionForTesting(Action action
) override
;
46 // views::DialogDelegate methods:
47 base::string16
GetDialogButtonLabel(ui::DialogButton button
) const override
;
48 base::string16
GetWindowTitle() const override
;
49 void DeleteDelegate() override
;
50 ui::ModalType
GetModalType() const override
;
51 bool Cancel() override
;
52 bool Accept() override
;
53 bool Close() override
;
54 views::View
* GetInitiallyFocusedView() override
;
55 views::View
* GetContentsView() override
;
56 views::Widget
* GetWidget() override
;
57 const views::Widget
* GetWidget() const override
;
59 // content::DownloadItem::Observer:
60 void OnDownloadUpdated(content::DownloadItem
* download
) override
;
63 base::string16
GetAcceptButtonTitle() const;
64 base::string16
GetCancelButtonTitle() const;
65 // The message lead is separated from the main text and is bolded.
66 base::string16
GetMessageLead() const;
67 base::string16
GetMessageBody() const;
68 void RunDone(Action action
);
70 content::DownloadItem
* download_
;
74 scoped_ptr
<ExperienceSamplingEvent
> sampling_event_
;
76 views::View
* contents_view_
;
79 DownloadDangerPromptViews::DownloadDangerPromptViews(
80 content::DownloadItem
* item
,
84 show_context_(show_context
),
86 contents_view_(NULL
) {
87 DCHECK(!done_
.is_null());
88 download_
->AddObserver(this);
90 contents_view_
= new views::View
;
92 views::GridLayout
* layout
= views::GridLayout::CreatePanel(contents_view_
);
93 contents_view_
->SetLayoutManager(layout
);
95 views::ColumnSet
* column_set
= layout
->AddColumnSet(0);
96 column_set
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 1,
97 views::GridLayout::FIXED
, kMessageWidth
, 0);
99 const base::string16 message_lead
= GetMessageLead();
101 if (!message_lead
.empty()) {
102 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
103 views::Label
* message_lead_label
= new views::Label(
104 message_lead
, rb
->GetFontList(ui::ResourceBundle::BoldFont
));
105 message_lead_label
->SetMultiLine(true);
106 message_lead_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
107 message_lead_label
->SetAllowCharacterBreak(true);
109 layout
->StartRow(0, 0);
110 layout
->AddView(message_lead_label
);
112 layout
->AddPaddingRow(0, kParagraphPadding
);
115 views::Label
* message_body_label
= new views::Label(GetMessageBody());
116 message_body_label
->SetMultiLine(true);
117 message_body_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
118 message_body_label
->SetAllowCharacterBreak(true);
120 layout
->StartRow(0, 0);
121 layout
->AddView(message_body_label
);
123 RecordOpenedDangerousConfirmDialog(download_
->GetDangerType());
125 // ExperienceSampling: A malicious download warning is being shown to the
126 // user, so we start a new SamplingEvent and track it.
127 sampling_event_
.reset(new ExperienceSamplingEvent(
128 ExperienceSamplingEvent::kDownloadDangerPrompt
,
130 item
->GetReferrerUrl(),
131 item
->GetBrowserContext()));
134 // DownloadDangerPrompt methods:
135 void DownloadDangerPromptViews::InvokeActionForTesting(Action action
) {
152 // views::DialogDelegate methods:
153 base::string16
DownloadDangerPromptViews::GetDialogButtonLabel(
154 ui::DialogButton button
) const {
156 case ui::DIALOG_BUTTON_OK
:
157 return GetAcceptButtonTitle();
159 case ui::DIALOG_BUTTON_CANCEL
:
160 return GetCancelButtonTitle();
163 return DialogDelegate::GetDialogButtonLabel(button
);
167 base::string16
DownloadDangerPromptViews::GetWindowTitle() const {
169 return l10n_util::GetStringUTF16(IDS_CONFIRM_KEEP_DANGEROUS_DOWNLOAD_TITLE
);
171 return l10n_util::GetStringUTF16(IDS_RESTORE_KEEP_DANGEROUS_DOWNLOAD_TITLE
);
174 void DownloadDangerPromptViews::DeleteDelegate() {
175 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
179 ui::ModalType
DownloadDangerPromptViews::GetModalType() const {
180 return ui::MODAL_TYPE_CHILD
;
183 bool DownloadDangerPromptViews::Cancel() {
184 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
185 // ExperienceSampling: User canceled the warning.
186 sampling_event_
->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny
);
191 bool DownloadDangerPromptViews::Accept() {
192 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
193 // ExperienceSampling: User proceeded through the warning.
194 sampling_event_
->CreateUserDecisionEvent(ExperienceSamplingEvent::kProceed
);
199 bool DownloadDangerPromptViews::Close() {
200 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
201 // ExperienceSampling: User canceled the warning.
202 sampling_event_
->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny
);
207 views::View
* DownloadDangerPromptViews::GetInitiallyFocusedView() {
208 return GetDialogClientView()->cancel_button();
211 views::View
* DownloadDangerPromptViews::GetContentsView() {
212 return contents_view_
;
215 views::Widget
* DownloadDangerPromptViews::GetWidget() {
216 return contents_view_
->GetWidget();
219 const views::Widget
* DownloadDangerPromptViews::GetWidget() const {
220 return contents_view_
->GetWidget();
223 // content::DownloadItem::Observer:
224 void DownloadDangerPromptViews::OnDownloadUpdated(
225 content::DownloadItem
* download
) {
226 // If the download is nolonger dangerous (accepted externally) or the download
227 // is in a terminal state, then the download danger prompt is no longer
229 if (!download_
->IsDangerous() || download_
->IsDone()) {
235 base::string16
DownloadDangerPromptViews::GetAcceptButtonTitle() const {
237 return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD
);
238 switch (download_
->GetDangerType()) {
239 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
:
240 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
:
241 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
: {
242 return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD_AGAIN_MALICIOUS
);
245 return l10n_util::GetStringUTF16(IDS_CONFIRM_DOWNLOAD_AGAIN
);
249 base::string16
DownloadDangerPromptViews::GetCancelButtonTitle() const {
251 return l10n_util::GetStringUTF16(IDS_CANCEL
);
252 switch (download_
->GetDangerType()) {
253 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
:
254 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
:
255 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
: {
256 return l10n_util::GetStringUTF16(IDS_CONFIRM_CANCEL_AGAIN_MALICIOUS
);
259 return l10n_util::GetStringUTF16(IDS_CANCEL
);
263 base::string16
DownloadDangerPromptViews::GetMessageLead() const {
264 if (!show_context_
) {
265 switch (download_
->GetDangerType()) {
266 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
:
267 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
:
268 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
:
269 return l10n_util::GetStringUTF16(
270 IDS_PROMPT_CONFIRM_KEEP_MALICIOUS_DOWNLOAD_LEAD
);
277 return base::string16();
280 base::string16
DownloadDangerPromptViews::GetMessageBody() const {
282 switch (download_
->GetDangerType()) {
283 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
: {
284 return l10n_util::GetStringFUTF16(
285 IDS_PROMPT_DANGEROUS_DOWNLOAD
,
286 download_
->GetFileNameToReportUser().LossyDisplayName());
288 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
: // Fall through
289 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
:
290 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
: {
291 return l10n_util::GetStringFUTF16(
292 IDS_PROMPT_MALICIOUS_DOWNLOAD_CONTENT
,
293 download_
->GetFileNameToReportUser().LossyDisplayName());
295 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
: {
296 return l10n_util::GetStringFUTF16(
297 IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT
,
298 download_
->GetFileNameToReportUser().LossyDisplayName());
300 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED
: {
301 return l10n_util::GetStringFUTF16(
302 IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS
,
303 download_
->GetFileNameToReportUser().LossyDisplayName());
305 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
:
306 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT
:
307 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED
:
308 case content::DOWNLOAD_DANGER_TYPE_MAX
: {
313 switch (download_
->GetDangerType()) {
314 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
:
315 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
:
316 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
: {
317 return l10n_util::GetStringUTF16(
318 IDS_PROMPT_CONFIRM_KEEP_MALICIOUS_DOWNLOAD_BODY
);
321 return l10n_util::GetStringUTF16(
322 IDS_PROMPT_CONFIRM_KEEP_DANGEROUS_DOWNLOAD
);
327 return base::string16();
330 void DownloadDangerPromptViews::RunDone(Action action
) {
331 // Invoking the callback can cause the download item state to change or cause
332 // the window to close, and |callback| refers to a member variable.
335 if (download_
!= NULL
) {
336 download_
->RemoveObserver(this);
345 DownloadDangerPrompt
* DownloadDangerPrompt::Create(
346 content::DownloadItem
* item
,
347 content::WebContents
* web_contents
,
349 const OnDone
& done
) {
350 DownloadDangerPromptViews
* download_danger_prompt
=
351 new DownloadDangerPromptViews(item
, show_context
, done
);
352 constrained_window::ShowWebModalDialogViews(download_danger_prompt
,
354 return download_danger_prompt
;