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