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