Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(427)

Side by Side Diff: chrome/browser/download/notification/download_notification_item.cc

Issue 1159363002: [Download Notification] Show preview if downloaded file is image (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase & use embedded server Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/download/notification/download_notification_item.h" 5 #include "chrome/browser/download/notification/download_notification_item.h"
6 6
7 #include "base/files/file_util.h"
7 #include "chrome/browser/browser_process.h" 8 #include "chrome/browser/browser_process.h"
8 #include "chrome/browser/download/download_crx_util.h" 9 #include "chrome/browser/download/download_crx_util.h"
9 #include "chrome/browser/download/download_item_model.h" 10 #include "chrome/browser/download/download_item_model.h"
10 #include "chrome/browser/download/notification/download_notification_manager.h" 11 #include "chrome/browser/download/notification/download_notification_manager.h"
11 #include "chrome/browser/notifications/notification.h" 12 #include "chrome/browser/notifications/notification.h"
12 #include "chrome/browser/notifications/notification_ui_manager.h" 13 #include "chrome/browser/notifications/notification_ui_manager.h"
13 #include "chrome/browser/notifications/profile_notification.h" 14 #include "chrome/browser/notifications/profile_notification.h"
14 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" 15 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
15 #include "chrome/common/url_constants.h" 16 #include "chrome/common/url_constants.h"
16 #include "chrome/grit/chromium_strings.h" 17 #include "chrome/grit/chromium_strings.h"
17 #include "chrome/grit/generated_resources.h" 18 #include "chrome/grit/generated_resources.h"
19 #include "components/mime_util/mime_util.h"
18 #include "content/public/browser/browser_context.h" 20 #include "content/public/browser/browser_context.h"
19 #include "content/public/browser/browser_thread.h" 21 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/download_interrupt_reasons.h" 22 #include "content/public/browser/download_interrupt_reasons.h"
21 #include "content/public/browser/download_item.h" 23 #include "content/public/browser/download_item.h"
22 #include "content/public/browser/page_navigator.h" 24 #include "content/public/browser/page_navigator.h"
23 #include "content/public/browser/web_contents.h" 25 #include "content/public/browser/web_contents.h"
24 #include "grit/theme_resources.h" 26 #include "grit/theme_resources.h"
27 #include "net/base/mime_util.h"
25 #include "ui/base/l10n/l10n_util.h" 28 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/base/resource/resource_bundle.h" 29 #include "ui/base/resource/resource_bundle.h"
30 #include "ui/gfx/codec/jpeg_codec.h"
31 #include "ui/gfx/image/image.h"
27 #include "ui/message_center/message_center.h" 32 #include "ui/message_center/message_center.h"
28 33
29 namespace { 34 namespace {
30 35
31 const char kDownloadNotificationNotifierId[] = 36 const char kDownloadNotificationNotifierId[] =
32 "chrome://downloads/notification/id-notifier"; 37 "chrome://downloads/notification/id-notifier";
33 38
39 // Maximum size of preview image. If the image exceeds this size, don't show the
40 // preview image.
41 const int64 kMaxImagePreviewSize = 10 * 1024 * 1024; // 10 MB
42
43 std::string ReadNotificationImage(const base::FilePath& file_path) {
44 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
45
46 std::string data;
47 bool ret = base::ReadFileToString(file_path, &data);
48 if (!ret)
49 return std::string();
50
51 DCHECK_LE(data.size(), static_cast<size_t>(kMaxImagePreviewSize));
52
53 return data;
54 }
55
34 } // anonymous namespace 56 } // anonymous namespace
35 57
36 DownloadNotificationItem::DownloadNotificationItem( 58 DownloadNotificationItem::DownloadNotificationItem(
37 content::DownloadItem* item, 59 content::DownloadItem* item,
38 DownloadNotificationManagerForProfile* manager) 60 DownloadNotificationManagerForProfile* manager)
39 : item_(item) { 61 : item_(item),
62 weak_factory_(this) {
40 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 63 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
41 64
42 message_center::RichNotificationData data; 65 message_center::RichNotificationData data;
43 // Creates the notification instance. |title| and |body| will be overridden 66 // Creates the notification instance. |title| and |body| will be overridden
44 // by UpdateNotificationData() below. 67 // by UpdateNotificationData() below.
45 notification_.reset(new Notification( 68 notification_.reset(new Notification(
46 message_center::NOTIFICATION_TYPE_PROGRESS, 69 message_center::NOTIFICATION_TYPE_PROGRESS,
47 GURL(kDownloadNotificationOrigin), // origin_url 70 GURL(kDownloadNotificationOrigin), // origin_url
48 base::string16(), // title 71 base::string16(), // title
49 base::string16(), // body 72 base::string16(), // body
50 bundle.GetImageNamed(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING), 73 bundle.GetImageNamed(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING),
51 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT, 74 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
52 kDownloadNotificationNotifierId), 75 kDownloadNotificationNotifierId),
53 base::string16(), // display_source 76 base::string16(), // display_source
54 base::UintToString(item_->GetId()), // tag 77 base::UintToString(item_->GetId()), // tag
55 data, watcher())); 78 data, watcher()));
56 79
57 notification_->set_progress(0); 80 notification_->set_progress(0);
58 notification_->set_never_timeout(false); 81 notification_->set_never_timeout(false);
59 } 82 }
60 83
61 DownloadNotificationItem::~DownloadNotificationItem() { 84 DownloadNotificationItem::~DownloadNotificationItem() {
85 if (image_decode_status_ == IN_PROGRESS)
86 ImageDecoder::Cancel(this);
62 } 87 }
63 88
64 void DownloadNotificationItem::OnNotificationClose() { 89 void DownloadNotificationItem::OnNotificationClose() {
65 visible_ = false; 90 visible_ = false;
91
92 if (image_decode_status_ == IN_PROGRESS) {
93 image_decode_status_ = NOT_STARTED;
94 ImageDecoder::Cancel(this);
95 }
66 } 96 }
67 97
68 void DownloadNotificationItem::OnNotificationClick() { 98 void DownloadNotificationItem::OnNotificationClick() {
69 if (item_->IsDangerous()) { 99 if (item_->IsDangerous()) {
70 #if defined(FULL_SAFE_BROWSING) 100 #if defined(FULL_SAFE_BROWSING)
71 DownloadCommands(item_).ExecuteCommand( 101 DownloadCommands(item_).ExecuteCommand(
72 DownloadCommands::LEARN_MORE_SCANNING); 102 DownloadCommands::LEARN_MORE_SCANNING);
73 #else 103 #else
74 CloseNotificationByUser(); 104 CloseNotificationByUser();
75 #endif 105 #endif
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
179 visible_ = true; 209 visible_ = true;
180 } 210 }
181 } 211 }
182 212
183 show_next_ = false; 213 show_next_ = false;
184 previous_download_state_ = item_->GetState(); 214 previous_download_state_ = item_->GetState();
185 } 215 }
186 216
187 void DownloadNotificationItem::UpdateNotificationData( 217 void DownloadNotificationItem::UpdateNotificationData(
188 NotificationUpdateType type) { 218 NotificationUpdateType type) {
219 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
220
189 DownloadItemModel model(item_); 221 DownloadItemModel model(item_);
190 DownloadCommands command(item_); 222 DownloadCommands command(item_);
191 223
192 if (item_->IsDangerous()) { 224 if (item_->IsDangerous()) {
193 notification_->set_type(message_center::NOTIFICATION_TYPE_BASE_FORMAT); 225 notification_->set_type(message_center::NOTIFICATION_TYPE_BASE_FORMAT);
194 notification_->set_title(GetTitle()); 226 notification_->set_title(GetTitle());
195 notification_->set_message(GetWarningText()); 227 notification_->set_message(GetWarningText());
196 228
197 // Show icon. 229 // Show icon.
198 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS); 230 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS);
199 } else { 231 } else {
200 notification_->set_title(GetTitle()); 232 notification_->set_title(GetTitle());
201 notification_->set_message(model.GetStatusText()); 233 notification_->set_message(model.GetStatusText());
202 234
203 bool is_off_the_record = item_->GetBrowserContext() && 235 bool is_off_the_record = item_->GetBrowserContext() &&
204 item_->GetBrowserContext()->IsOffTheRecord(); 236 item_->GetBrowserContext()->IsOffTheRecord();
205 237
206 switch (item_->GetState()) { 238 switch (item_->GetState()) {
207 case content::DownloadItem::IN_PROGRESS: 239 case content::DownloadItem::IN_PROGRESS:
208 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS); 240 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
209 notification_->set_progress(item_->PercentComplete()); 241 notification_->set_progress(item_->PercentComplete());
210 if (is_off_the_record) { 242 if (is_off_the_record) {
211 // TODO(yoshiki): Replace the tentative image. 243 // TODO(yoshiki): Replace the tentative image.
212 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO); 244 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
213 } else { 245 } else {
214 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING); 246 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
215 } 247 }
216 break; 248 break;
217 case content::DownloadItem::COMPLETE: 249 case content::DownloadItem::COMPLETE:
218 DCHECK(item_->IsDone()); 250 DCHECK(item_->IsDone());
219 251
220 // Shows a notifiation as progress type once so the visible content will 252 // Shows a notifiation as progress type once so the visible content will
221 // be updated. 253 // be updated.
222 // Note: only progress-type notification's content will be updated 254 // Note: only progress-type notification's content will be updated
223 // immediately when the message center is visible. 255 // immediately when the message center is visible.
224 if (type == UPDATE_AND_POPUP) { 256 if (type == UPDATE_AND_POPUP) {
225 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS); 257 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
226 } else { 258 } else {
227 notification_->set_type( 259 notification_->set_type(
228 message_center::NOTIFICATION_TYPE_BASE_FORMAT); 260 message_center::NOTIFICATION_TYPE_BASE_FORMAT);
229 } 261 }
230 262
231 notification_->set_progress(100); 263 notification_->set_progress(100);
232 264
233 if (is_off_the_record) { 265 if (is_off_the_record) {
234 // TODO(yoshiki): Replace the tentative image. 266 // TODO(yoshiki): Replace the tentative image.
235 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO); 267 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
236 } else { 268 } else {
237 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING); 269 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
238 } 270 }
239 break; 271 break;
240 case content::DownloadItem::CANCELLED: 272 case content::DownloadItem::CANCELLED:
241 // Confgirms that a download is cancelled by user action. 273 // Confgirms that a download is cancelled by user action.
242 DCHECK(item_->GetLastReason() == 274 DCHECK(item_->GetLastReason() ==
243 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED || 275 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED ||
244 item_->GetLastReason() == 276 item_->GetLastReason() ==
245 content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN); 277 content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN);
246 278
247 CloseNotificationByUser(); 279 CloseNotificationByUser();
248 return; // Skips the remaining since the notification has closed. 280 return; // Skips the remaining since the notification has closed.
249 case content::DownloadItem::INTERRUPTED: 281 case content::DownloadItem::INTERRUPTED:
250 // Shows a notifiation as progress type once so the visible content will 282 // Shows a notifiation as progress type once so the visible content will
251 // be updated. (same as the case of type = COMPLETE) 283 // be updated. (same as the case of type = COMPLETE)
252 if (type == UPDATE_AND_POPUP) { 284 if (type == UPDATE_AND_POPUP) {
253 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS); 285 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
254 } else { 286 } else {
255 notification_->set_type( 287 notification_->set_type(
256 message_center::NOTIFICATION_TYPE_BASE_FORMAT); 288 message_center::NOTIFICATION_TYPE_BASE_FORMAT);
257 } 289 }
258 290
259 notification_->set_progress(0); 291 notification_->set_progress(0);
260 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_WARNING); 292 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_WARNING);
261 break; 293 break;
262 case content::DownloadItem::MAX_DOWNLOAD_STATE: // sentinel 294 case content::DownloadItem::MAX_DOWNLOAD_STATE: // sentinel
263 NOTREACHED(); 295 NOTREACHED();
264 } 296 }
265 } 297 }
266 298
267 std::vector<message_center::ButtonInfo> notification_actions; 299 std::vector<message_center::ButtonInfo> notification_actions;
268 scoped_ptr<std::vector<DownloadCommands::Command>> actions( 300 scoped_ptr<std::vector<DownloadCommands::Command>> actions(
269 GetExtraActions().Pass()); 301 GetExtraActions().Pass());
270 302
271 button_actions_.reset(new std::vector<DownloadCommands::Command>); 303 button_actions_.reset(new std::vector<DownloadCommands::Command>);
272 for (auto it = actions->begin(); it != actions->end(); it++) { 304 for (auto it = actions->begin(); it != actions->end(); it++) {
273 button_actions_->push_back(*it); 305 button_actions_->push_back(*it);
274 message_center::ButtonInfo button_info = 306 message_center::ButtonInfo button_info =
275 message_center::ButtonInfo(GetCommandLabel(*it)); 307 message_center::ButtonInfo(GetCommandLabel(*it));
276 button_info.icon = command.GetCommandIcon(*it); 308 button_info.icon = command.GetCommandIcon(*it);
277 notification_actions.push_back(button_info); 309 notification_actions.push_back(button_info);
278 } 310 }
279 notification_->set_buttons(notification_actions); 311 notification_->set_buttons(notification_actions);
280 312
281 if (item_->IsDone()) {
282 // TODO(yoshiki): If the downloaded file is an image, show the thumbnail.
283 }
284
285 if (type == ADD) { 313 if (type == ADD) {
286 g_browser_process->notification_ui_manager()-> 314 g_browser_process->notification_ui_manager()->
287 Add(*notification_, profile()); 315 Add(*notification_, profile());
288 } else if (type == UPDATE || type == UPDATE_AND_POPUP) { 316 } else if (type == UPDATE || type == UPDATE_AND_POPUP) {
289 g_browser_process->notification_ui_manager()-> 317 g_browser_process->notification_ui_manager()->
290 Update(*notification_, profile()); 318 Update(*notification_, profile());
291 319
292 if (type == UPDATE_AND_POPUP) { 320 if (type == UPDATE_AND_POPUP) {
293 CloseNotificationByNonUser(); 321 CloseNotificationByNonUser();
294 // Changes the type from PROGRESS to BASE_FORMAT. 322 // Changes the type from PROGRESS to BASE_FORMAT.
295 notification_->set_type(message_center::NOTIFICATION_TYPE_BASE_FORMAT); 323 notification_->set_type(message_center::NOTIFICATION_TYPE_BASE_FORMAT);
296 g_browser_process->notification_ui_manager()-> 324 g_browser_process->notification_ui_manager()->
297 Add(*notification_, profile()); 325 Add(*notification_, profile());
298 } 326 }
299 } else { 327 } else {
300 NOTREACHED(); 328 NOTREACHED();
301 } 329 }
330
331 if (item_->IsDone() && image_decode_status_ == NOT_STARTED) {
332 // TODO(yoshiki): Add an UMA to collect statistics of image file sizes.
333
334 if (item_->GetReceivedBytes() > kMaxImagePreviewSize)
335 return;
336
337 DCHECK(notification_->image().IsEmpty());
338
339 image_decode_status_ = IN_PROGRESS;
340
341 bool maybe_image = false;
342 if (mime_util::IsSupportedImageMimeType(item_->GetMimeType())) {
343 maybe_image = true;
344 } else {
345 std::string mime;
346 base::FilePath::StringType extension_with_dot =
347 item_->GetTargetFilePath().FinalExtension();
348 if (!extension_with_dot.empty() &&
349 net::GetWellKnownMimeTypeFromExtension(extension_with_dot.substr(1),
350 &mime) &&
351 mime_util::IsSupportedImageMimeType(mime)) {
352 maybe_image = true;
353 }
354 }
355
356 if (maybe_image) {
357 base::FilePath file_path = item_->GetFullPath();
358 base::PostTaskAndReplyWithResult(
359 content::BrowserThread::GetBlockingPool(), FROM_HERE,
360 base::Bind(&ReadNotificationImage, file_path),
361 base::Bind(&DownloadNotificationItem::OnImageLoaded,
362 weak_factory_.GetWeakPtr()));
363 }
364 }
302 } 365 }
303 366
304 void DownloadNotificationItem::OnDownloadRemoved(content::DownloadItem* item) { 367 void DownloadNotificationItem::OnDownloadRemoved(content::DownloadItem* item) {
305 // The given |item| may be already free'd. 368 // The given |item| may be already free'd.
306 DCHECK_EQ(item, item_); 369 DCHECK_EQ(item, item_);
307 370
308 // Removing the notification causes calling |NotificationDelegate::Close()|. 371 // Removing the notification causes calling |NotificationDelegate::Close()|.
309 if (g_browser_process->notification_ui_manager()) { 372 if (g_browser_process->notification_ui_manager()) {
310 g_browser_process->notification_ui_manager()->CancelById( 373 g_browser_process->notification_ui_manager()->CancelById(
311 watcher()->id(), NotificationUIManager::GetProfileID(profile())); 374 watcher()->id(), NotificationUIManager::GetProfileID(profile()));
312 } 375 }
313 376
314 item_ = nullptr; 377 item_ = nullptr;
315 } 378 }
316 379
317 void DownloadNotificationItem::SetNotificationImage(int resource_id) { 380 void DownloadNotificationItem::SetNotificationIcon(int resource_id) {
318 if (image_resource_id_ == resource_id) 381 if (image_resource_id_ == resource_id)
319 return; 382 return;
320 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 383 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
321 image_resource_id_ = resource_id; 384 image_resource_id_ = resource_id;
322 notification_->set_icon(bundle.GetImageNamed(image_resource_id_)); 385 notification_->set_icon(bundle.GetImageNamed(image_resource_id_));
323 } 386 }
324 387
388 void DownloadNotificationItem::OnImageLoaded(const std::string& image_data) {
389 if (image_data.empty())
390 return;
391
392 // TODO(yoshiki): Set option to reduce the image size to supress memory usage.
393 ImageDecoder::Start(this, image_data);
394 }
395
396 void DownloadNotificationItem::OnImageDecoded(const SkBitmap& decoded_image) {
397 gfx::Image image = gfx::Image::CreateFrom1xBitmap(decoded_image);
398 notification_->set_image(image);
399 image_decode_status_ = DONE;
400 UpdateNotificationData(UPDATE);
401 }
402
403 void DownloadNotificationItem::OnDecodeImageFailed() {
404 DCHECK(notification_->image().IsEmpty());
405
406 image_decode_status_ = FAILED;
407 UpdateNotificationData(UPDATE);
408 }
409
325 scoped_ptr<std::vector<DownloadCommands::Command>> 410 scoped_ptr<std::vector<DownloadCommands::Command>>
326 DownloadNotificationItem::GetExtraActions() const { 411 DownloadNotificationItem::GetExtraActions() const {
327 scoped_ptr<std::vector<DownloadCommands::Command>> actions( 412 scoped_ptr<std::vector<DownloadCommands::Command>> actions(
328 new std::vector<DownloadCommands::Command>()); 413 new std::vector<DownloadCommands::Command>());
329 414
330 if (item_->IsDangerous()) { 415 if (item_->IsDangerous()) {
331 actions->push_back(DownloadCommands::DISCARD); 416 actions->push_back(DownloadCommands::DISCARD);
332 actions->push_back(DownloadCommands::KEEP); 417 actions->push_back(DownloadCommands::KEEP);
333 return actions.Pass(); 418 return actions.Pass();
334 } 419 }
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 Browser* DownloadNotificationItem::GetBrowser() const { 554 Browser* DownloadNotificationItem::GetBrowser() const {
470 chrome::ScopedTabbedBrowserDisplayer browser_displayer( 555 chrome::ScopedTabbedBrowserDisplayer browser_displayer(
471 profile(), chrome::GetActiveDesktop()); 556 profile(), chrome::GetActiveDesktop());
472 DCHECK(browser_displayer.browser()); 557 DCHECK(browser_displayer.browser());
473 return browser_displayer.browser(); 558 return browser_displayer.browser();
474 } 559 }
475 560
476 Profile* DownloadNotificationItem::profile() const { 561 Profile* DownloadNotificationItem::profile() const {
477 return Profile::FromBrowserContext(item_->GetBrowserContext()); 562 return Profile::FromBrowserContext(item_->GetBrowserContext());
478 } 563 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698