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