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

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: 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 "base/strings/string_number_conversions.h" 8 #include "base/strings/string_number_conversions.h"
8 #include "chrome/browser/browser_process.h" 9 #include "chrome/browser/browser_process.h"
9 #include "chrome/browser/download/download_crx_util.h" 10 #include "chrome/browser/download/download_crx_util.h"
10 #include "chrome/browser/download/download_item_model.h" 11 #include "chrome/browser/download/download_item_model.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"
18 #include "content/public/browser/browser_context.h" 19 #include "content/public/browser/browser_context.h"
19 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/download_interrupt_reasons.h" 21 #include "content/public/browser/download_interrupt_reasons.h"
21 #include "content/public/browser/download_item.h" 22 #include "content/public/browser/download_item.h"
22 #include "content/public/browser/page_navigator.h" 23 #include "content/public/browser/page_navigator.h"
23 #include "content/public/browser/web_contents.h" 24 #include "content/public/browser/web_contents.h"
24 #include "grit/theme_resources.h" 25 #include "grit/theme_resources.h"
26 #include "net/base/mime_util.h"
27 #include "third_party/skia/include/core/SkBitmap.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 gfx::Image ReadNotificationImage(
44 const std::string type, const base::FilePath file_path) {
45 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
asanka 2015/06/03 03:02:04 Use DCHECK_CURRENTLY_ON instead.
yoshiki 2015/06/04 04:13:22 Done.
46
47 gfx::Image image;
48
49 std::string data;
50 bool ret = base::ReadFileToString(file_path, &data);
51 if (!ret)
52 return image;
53
54 if (data.size() > kMaxImagePreviewSize)
55 return image;
56
57 if (type == "png") {
58 image = gfx::Image::CreateFrom1xPNGBytes(
59 reinterpret_cast<const unsigned char*>(data.data()),
60 data.length());
61 } else if (type == "jpg" || type == "jpeg") {
62 scoped_ptr<SkBitmap> decoded_jpeg(gfx::JPEGCodec::Decode(
63 reinterpret_cast<const unsigned char*>(data.data()),
64 data.length()));
65 if (decoded_jpeg) {
66 image = gfx::Image::CreateFrom1xBitmap(*decoded_jpeg);
67 }
68 } else {
69 NOTREACHED(); // Type should have been checked in the caller.
70 }
71
72 // TODO(yoshiki): Shrink the image for saving memory.
73
74 return image;
75 }
76
34 } // anonymous namespace 77 } // anonymous namespace
35 78
36 // static 79 // static
37 const char DownloadNotificationItem::kDownloadNotificationOrigin[] = 80 const char DownloadNotificationItem::kDownloadNotificationOrigin[] =
38 "chrome://downloads"; 81 "chrome://downloads";
39 82
40 // static 83 // static
41 StubNotificationUIManager* 84 StubNotificationUIManager*
42 DownloadNotificationItem::stub_notification_ui_manager_for_testing_ = 85 DownloadNotificationItem::stub_notification_ui_manager_for_testing_ =
43 nullptr; 86 nullptr;
(...skipping 26 matching lines...) Expand all
70 std::string DownloadNotificationItem::NotificationWatcher::id() const { 113 std::string DownloadNotificationItem::NotificationWatcher::id() const {
71 return base::UintToString(item_->item_->GetId()); 114 return base::UintToString(item_->item_->GetId());
72 } 115 }
73 116
74 DownloadNotificationItem::DownloadNotificationItem(content::DownloadItem* item, 117 DownloadNotificationItem::DownloadNotificationItem(content::DownloadItem* item,
75 Profile* profile, 118 Profile* profile,
76 Delegate* delegate) 119 Delegate* delegate)
77 : profile_(profile), 120 : profile_(profile),
78 watcher_(new NotificationWatcher(this)), 121 watcher_(new NotificationWatcher(this)),
79 item_(item), 122 item_(item),
80 delegate_(delegate) { 123 delegate_(delegate),
124 weak_factory_(this) {
81 item->AddObserver(this); 125 item->AddObserver(this);
82 126
83 // Notify that the instance is just created. 127 // Notify that the instance is just created.
84 delegate_->OnCreated(this); 128 delegate_->OnCreated(this);
85 129
86 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 130 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
87 131
88 message_center::RichNotificationData data; 132 message_center::RichNotificationData data;
89 // Creates the notification instance. |title| and |body| will be overridden 133 // Creates the notification instance. |title| and |body| will be overridden
90 // by UpdateNotificationData() below. 134 // by UpdateNotificationData() below.
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 // because it's by user action. So, we request closing of it directlly to 237 // because it's by user action. So, we request closing of it directlly to
194 // MessageCenter instance. 238 // MessageCenter instance.
195 // Note that: this calling has no side-effect even when the message center 239 // Note that: this calling has no side-effect even when the message center
196 // is not opened. 240 // is not opened.
197 g_browser_process->message_center()->RemoveNotification( 241 g_browser_process->message_center()->RemoveNotification(
198 notification_id_in_message_center, true /* by_user */); 242 notification_id_in_message_center, true /* by_user */);
199 } 243 }
200 244
201 void DownloadNotificationItem::UpdateNotificationData( 245 void DownloadNotificationItem::UpdateNotificationData(
202 NotificationUpdateType type) { 246 NotificationUpdateType type) {
247 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
248
203 DownloadItemModel model(item_); 249 DownloadItemModel model(item_);
204 DownloadCommands command(item_); 250 DownloadCommands command(item_);
205 251
206 if (previous_download_state_ != content::DownloadItem::IN_PROGRESS) { 252 if (previous_download_state_ != content::DownloadItem::IN_PROGRESS) {
207 if (item_->GetState() == content::DownloadItem::IN_PROGRESS) 253 if (item_->GetState() == content::DownloadItem::IN_PROGRESS)
208 delegate_->OnDownloadStarted(this); 254 delegate_->OnDownloadStarted(this);
209 } else { 255 } else {
210 if (item_->GetState() != content::DownloadItem::IN_PROGRESS) 256 if (item_->GetState() != content::DownloadItem::IN_PROGRESS)
211 delegate_->OnDownloadStopped(this); 257 delegate_->OnDownloadStopped(this);
212 } 258 }
213 259
214 if (item_->IsDangerous()) { 260 if (item_->IsDangerous()) {
215 notification_->set_type(message_center::NOTIFICATION_TYPE_BASE_FORMAT); 261 notification_->set_type(message_center::NOTIFICATION_TYPE_BASE_FORMAT);
216 notification_->set_title(GetTitle()); 262 notification_->set_title(GetTitle());
217 notification_->set_message(GetWarningText()); 263 notification_->set_message(GetWarningText());
218 264
219 // Show icon. 265 // Show icon.
220 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS); 266 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS);
221 } else { 267 } else {
222 notification_->set_title(GetTitle()); 268 notification_->set_title(GetTitle());
223 notification_->set_message(model.GetStatusText()); 269 notification_->set_message(model.GetStatusText());
224 270
225 bool is_off_the_record = item_->GetBrowserContext() && 271 bool is_off_the_record = item_->GetBrowserContext() &&
226 item_->GetBrowserContext()->IsOffTheRecord(); 272 item_->GetBrowserContext()->IsOffTheRecord();
227 273
228 switch (item_->GetState()) { 274 switch (item_->GetState()) {
229 case content::DownloadItem::IN_PROGRESS: 275 case content::DownloadItem::IN_PROGRESS:
230 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS); 276 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
231 notification_->set_progress(item_->PercentComplete()); 277 notification_->set_progress(item_->PercentComplete());
232 if (is_off_the_record) { 278 if (is_off_the_record) {
233 // TODO(yoshiki): Replace the tentative image. 279 // TODO(yoshiki): Replace the tentative image.
234 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO); 280 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
235 } else { 281 } else {
236 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING); 282 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
237 } 283 }
238 break; 284 break;
239 case content::DownloadItem::COMPLETE: 285 case content::DownloadItem::COMPLETE:
240 DCHECK(item_->IsDone()); 286 DCHECK(item_->IsDone());
241 287
242 // Shows a notifiation as progress type once so the visible content will 288 // Shows a notifiation as progress type once so the visible content will
243 // be updated. 289 // be updated.
244 // Note: only progress-type notification's content will be updated 290 // Note: only progress-type notification's content will be updated
245 // immediately when the message center is visible. 291 // immediately when the message center is visible.
246 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS); 292 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
247 notification_->set_progress(100); 293 notification_->set_progress(100);
248 294
249 if (is_off_the_record) { 295 if (is_off_the_record) {
250 // TODO(yoshiki): Replace the tentative image. 296 // TODO(yoshiki): Replace the tentative image.
251 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO); 297 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
252 } else { 298 } else {
253 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING); 299 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
254 } 300 }
255 break; 301 break;
256 case content::DownloadItem::CANCELLED: 302 case content::DownloadItem::CANCELLED:
257 // Confgirms that a download is cancelled by user action. 303 // Confgirms that a download is cancelled by user action.
258 DCHECK(item_->GetLastReason() == 304 DCHECK(item_->GetLastReason() ==
259 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED || 305 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED ||
260 item_->GetLastReason() == 306 item_->GetLastReason() ==
261 content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN); 307 content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN);
262 308
263 CloseNotificationByUser(); 309 CloseNotificationByUser();
264 310
265 previous_download_state_ = item_->GetState(); 311 previous_download_state_ = item_->GetState();
266 return; // Skips the remaining since the notification has closed. 312 return; // Skips the remaining since the notification has closed.
267 case content::DownloadItem::INTERRUPTED: 313 case content::DownloadItem::INTERRUPTED:
268 // Shows a notifiation as progress type once so the visible content will 314 // Shows a notifiation as progress type once so the visible content will
269 // be updated. (same as the case of type = COMPLETE) 315 // be updated. (same as the case of type = COMPLETE)
270 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS); 316 notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
271 notification_->set_progress(0); 317 notification_->set_progress(0);
272 SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_WARNING); 318 SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_WARNING);
273 break; 319 break;
274 case content::DownloadItem::MAX_DOWNLOAD_STATE: // sentinel 320 case content::DownloadItem::MAX_DOWNLOAD_STATE: // sentinel
275 NOTREACHED(); 321 NOTREACHED();
276 } 322 }
277 } 323 }
278 324
279 std::vector<message_center::ButtonInfo> notification_actions; 325 std::vector<message_center::ButtonInfo> notification_actions;
280 scoped_ptr<std::vector<DownloadCommands::Command>> actions( 326 scoped_ptr<std::vector<DownloadCommands::Command>> actions(
281 GetExtraActions().Pass()); 327 GetExtraActions().Pass());
282 328
283 button_actions_.reset(new std::vector<DownloadCommands::Command>); 329 button_actions_.reset(new std::vector<DownloadCommands::Command>);
284 for (auto it = actions->begin(); it != actions->end(); it++) { 330 for (auto it = actions->begin(); it != actions->end(); it++) {
285 button_actions_->push_back(*it); 331 button_actions_->push_back(*it);
286 message_center::ButtonInfo button_info = 332 message_center::ButtonInfo button_info =
287 message_center::ButtonInfo(GetCommandLabel(*it)); 333 message_center::ButtonInfo(GetCommandLabel(*it));
288 button_info.icon = command.GetCommandIcon(*it); 334 button_info.icon = command.GetCommandIcon(*it);
289 notification_actions.push_back(button_info); 335 notification_actions.push_back(button_info);
290 } 336 }
291 notification_->set_buttons(notification_actions); 337 notification_->set_buttons(notification_actions);
292 338
293 if (item_->IsDone()) {
294 // TODO(yoshiki): If the downloaded file is an image, show the thumbnail.
295 }
296
297 if (type == ADD_NEW) { 339 if (type == ADD_NEW) {
298 notification_ui_manager()->Add(*notification_, profile_); 340 notification_ui_manager()->Add(*notification_, profile_);
299 } else if (type == UPDATE_EXISTING) { 341 } else if (type == UPDATE_EXISTING) {
300 notification_ui_manager()->Update(*notification_, profile_); 342 notification_ui_manager()->Update(*notification_, profile_);
301 343
302 // When the download is just completed (or interrupted), close the 344 // When the download is just completed (or interrupted), close the
303 // notification once and re-show it immediately so it'll pop up. 345 // notification once and re-show it immediately so it'll pop up.
304 if ((item_->GetState() == content::DownloadItem::COMPLETE && 346 if ((item_->GetState() == content::DownloadItem::COMPLETE &&
305 previous_download_state_ != content::DownloadItem::COMPLETE) || 347 previous_download_state_ != content::DownloadItem::COMPLETE) ||
306 (item_->GetState() == content::DownloadItem::INTERRUPTED && 348 (item_->GetState() == content::DownloadItem::INTERRUPTED &&
307 previous_download_state_ != content::DownloadItem::INTERRUPTED)) { 349 previous_download_state_ != content::DownloadItem::INTERRUPTED)) {
308 CloseNotificationByNonUser(); 350 CloseNotificationByNonUser();
309 // Changes the type from PROGRESS to BASE_FORMAT. 351 // Changes the type from PROGRESS to BASE_FORMAT.
310 notification_->set_type(message_center::NOTIFICATION_TYPE_BASE_FORMAT); 352 notification_->set_type(message_center::NOTIFICATION_TYPE_BASE_FORMAT);
311 notification_ui_manager()->Add(*notification_, profile_); 353 notification_ui_manager()->Add(*notification_, profile_);
312 } 354 }
313 } else { 355 } else {
314 NOTREACHED(); 356 NOTREACHED();
315 } 357 }
316 358
317 previous_download_state_ = item_->GetState(); 359 previous_download_state_ = item_->GetState();
360
361 if (item_->IsDone() && !set_image_ &&
362 item_->GetTotalBytes() <= kMaxImagePreviewSize) {
363 DCHECK(notification_->image().IsEmpty());
364
365 set_image_ = true;
366
367 std::string type;
368 std::string mime_topleveltype;
369 std::string mime_subtype;
370 if (net::ParseMimeTypeWithoutParameter(
371 item_->GetMimeType(), &mime_topleveltype, &mime_subtype) &&
372 mime_topleveltype == "image" &&
373 (mime_subtype == "png" || mime_subtype == "jpg" ||
374 mime_subtype == "jpeg")) {
375 type = mime_subtype;
376 } else {
377 std::string dot_extension = item_->GetTargetFilePath().FinalExtension();
378 if (!dot_extension.empty()) {
379 std::string extension = dot_extension.substr(1);
380 if (extension == "png" || extension == "jpg" || extension == "jpeg")
381 type = extension;
382 }
383 }
384
385 if (!type.empty()) {
386 base::FilePath file_path = item_->GetFullPath();
387 content::BrowserThread::PostTaskAndReplyWithResult(
388 content::BrowserThread::FILE,
389 FROM_HERE,
390 base::Bind(&ReadNotificationImage,
391 type,
392 file_path),
393 base::Bind(&DownloadNotificationItem::SetNotificationImage,
394 weak_factory_.GetWeakPtr()));
395 }
396 }
318 } 397 }
319 398
320 void DownloadNotificationItem::OnDownloadOpened(content::DownloadItem* item) { 399 void DownloadNotificationItem::OnDownloadOpened(content::DownloadItem* item) {
321 DCHECK_EQ(item, item_); 400 DCHECK_EQ(item, item_);
322 // Do nothing. 401 // Do nothing.
323 } 402 }
324 403
325 void DownloadNotificationItem::OnDownloadRemoved(content::DownloadItem* item) { 404 void DownloadNotificationItem::OnDownloadRemoved(content::DownloadItem* item) {
326 DCHECK_EQ(item, item_); 405 DCHECK_EQ(item, item_);
327 406
328 // Removing the notification causes calling |NotificationDelegate::Close()|. 407 // Removing the notification causes calling |NotificationDelegate::Close()|.
329 notification_ui_manager()->CancelById( 408 notification_ui_manager()->CancelById(
330 watcher_->id(), NotificationUIManager::GetProfileID(profile_)); 409 watcher_->id(), NotificationUIManager::GetProfileID(profile_));
331 delegate_->OnDownloadRemoved(this); 410 delegate_->OnDownloadRemoved(this);
332 } 411 }
333 412
334 void DownloadNotificationItem::OnDownloadDestroyed( 413 void DownloadNotificationItem::OnDownloadDestroyed(
335 content::DownloadItem* item) { 414 content::DownloadItem* item) {
336 DCHECK_EQ(item, item_); 415 DCHECK_EQ(item, item_);
337 416
338 item_ = nullptr; 417 item_ = nullptr;
339 } 418 }
340 419
341 void DownloadNotificationItem::SetNotificationImage(int resource_id) { 420 void DownloadNotificationItem::SetNotificationIcon(int resource_id) {
342 if (image_resource_id_ == resource_id) 421 if (image_resource_id_ == resource_id)
343 return; 422 return;
344 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 423 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
345 image_resource_id_ = resource_id; 424 image_resource_id_ = resource_id;
346 notification_->set_icon(bundle.GetImageNamed(image_resource_id_)); 425 notification_->set_icon(bundle.GetImageNamed(image_resource_id_));
347 } 426 }
348 427
428 void DownloadNotificationItem::SetNotificationImage(gfx::Image image) {
429 if (image.IsEmpty())
430 return;
431 notification_->set_image(image);
432 UpdateNotificationData(UPDATE_EXISTING);
433 }
434
349 NotificationUIManager* DownloadNotificationItem::notification_ui_manager() 435 NotificationUIManager* DownloadNotificationItem::notification_ui_manager()
350 const { 436 const {
351 if (stub_notification_ui_manager_for_testing_) { 437 if (stub_notification_ui_manager_for_testing_) {
352 return stub_notification_ui_manager_for_testing_; 438 return stub_notification_ui_manager_for_testing_;
353 } 439 }
354 return g_browser_process->notification_ui_manager(); 440 return g_browser_process->notification_ui_manager();
355 } 441 }
356 442
357 scoped_ptr<std::vector<DownloadCommands::Command>> 443 scoped_ptr<std::vector<DownloadCommands::Command>>
358 DownloadNotificationItem::GetExtraActions() const { 444 DownloadNotificationItem::GetExtraActions() const {
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
497 NOTREACHED(); 583 NOTREACHED();
498 return base::string16(); 584 return base::string16();
499 } 585 }
500 586
501 Browser* DownloadNotificationItem::GetBrowser() { 587 Browser* DownloadNotificationItem::GetBrowser() {
502 chrome::ScopedTabbedBrowserDisplayer browser_displayer( 588 chrome::ScopedTabbedBrowserDisplayer browser_displayer(
503 profile_, chrome::GetActiveDesktop()); 589 profile_, chrome::GetActiveDesktop());
504 DCHECK(browser_displayer.browser()); 590 DCHECK(browser_displayer.browser());
505 return browser_displayer.browser(); 591 return browser_displayer.browser();
506 } 592 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698