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

Side by Side Diff: chrome/browser/extensions/api/notifications/notifications_api.cc

Issue 291193009: Reland: Allow high-res bitmaps to be passed in from notifications API. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/extensions/api/notifications/notifications_api.h" 5 #include "chrome/browser/extensions/api/notifications/notifications_api.h"
6 6
7 #include "base/callback.h" 7 #include "base/callback.h"
8 #include "base/guid.h" 8 #include "base/guid.h"
9 #include "base/rand_util.h" 9 #include "base/rand_util.h"
10 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/utf_string_conversions.h" 11 #include "base/strings/utf_string_conversions.h"
12 #include "base/time/time.h" 12 #include "base/time/time.h"
13 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/notifications/desktop_notification_service.h" 14 #include "chrome/browser/notifications/desktop_notification_service.h"
15 #include "chrome/browser/notifications/desktop_notification_service_factory.h" 15 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
16 #include "chrome/browser/notifications/notification.h" 16 #include "chrome/browser/notifications/notification.h"
17 #include "chrome/browser/notifications/notification_ui_manager.h" 17 #include "chrome/browser/notifications/notification_ui_manager.h"
18 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/chrome_version_info.h" 19 #include "chrome/common/chrome_version_info.h"
20 #include "chrome/common/extensions/api/notifications/notification_style.h"
20 #include "content/public/browser/render_process_host.h" 21 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/render_view_host.h" 22 #include "content/public/browser/render_view_host.h"
22 #include "content/public/browser/web_contents.h" 23 #include "content/public/browser/web_contents.h"
23 #include "extensions/browser/event_router.h" 24 #include "extensions/browser/event_router.h"
24 #include "extensions/common/extension.h" 25 #include "extensions/common/extension.h"
25 #include "extensions/common/features/feature.h" 26 #include "extensions/common/features/feature.h"
26 #include "third_party/skia/include/core/SkBitmap.h" 27 #include "third_party/skia/include/core/SkBitmap.h"
28 #include "ui/base/layout.h"
27 #include "ui/gfx/image/image.h" 29 #include "ui/gfx/image/image.h"
28 #include "ui/gfx/image/image_skia.h" 30 #include "ui/gfx/image/image_skia.h"
29 #include "ui/gfx/image/image_skia_rep.h" 31 #include "ui/gfx/image/image_skia_rep.h"
30 #include "ui/message_center/message_center_style.h" 32 #include "ui/message_center/message_center_style.h"
31 #include "ui/message_center/notifier_settings.h" 33 #include "ui/message_center/notifier_settings.h"
32 #include "url/gurl.h" 34 #include "url/gurl.h"
33 35
34 namespace extensions { 36 namespace extensions {
35 37
36 namespace notifications = api::notifications; 38 namespace notifications = api::notifications;
37 39
38 namespace { 40 namespace {
39 41
40 const char kMissingRequiredPropertiesForCreateNotification[] = 42 const char kMissingRequiredPropertiesForCreateNotification[] =
41 "Some of the required properties are missing: type, iconUrl, title and " 43 "Some of the required properties are missing: type, iconUrl, title and "
42 "message."; 44 "message.";
45 const char kUnableToDecodeIconError[] =
46 "Unable to successfully use the provided image.";
43 const char kUnexpectedProgressValueForNonProgressType[] = 47 const char kUnexpectedProgressValueForNonProgressType[] =
44 "The progress value should not be specified for non-progress notification"; 48 "The progress value should not be specified for non-progress notification";
45 const char kInvalidProgressValue[] = 49 const char kInvalidProgressValue[] =
46 "The progress value should range from 0 to 100"; 50 "The progress value should range from 0 to 100";
51 const char kExtraListItemsProvided[] =
52 "List items provided for notification type != list";
53 const char kExtraImageProvided[] =
54 "Image resource provided for notification type != image";
47 55
48 // Converts an object with width, height, and data in RGBA format into an 56 // Converts an object with width, height, and data in RGBA format into an
49 // gfx::Image (in ARGB format). 57 // gfx::Image (in ARGB format).
50 bool NotificationBitmapToGfxImage( 58 bool NotificationBitmapToGfxImage(
59 float max_scale,
60 const gfx::Size& target_size_dips,
51 api::notifications::NotificationBitmap* notification_bitmap, 61 api::notifications::NotificationBitmap* notification_bitmap,
52 gfx::Image* return_image) { 62 gfx::Image* return_image) {
53 if (!notification_bitmap) 63 if (!notification_bitmap)
54 return false; 64 return false;
55 65
56 // Ensure a sane set of dimensions. 66 const int max_device_pixel_width = target_size_dips.width() * max_scale;
57 const int max_width = message_center::kNotificationPreferredImageWidth; 67 const int max_device_pixel_height = target_size_dips.height() * max_scale;
58 const int max_height = message_center::kNotificationPreferredImageHeight; 68
59 const int BYTES_PER_PIXEL = 4; 69 const int BYTES_PER_PIXEL = 4;
60 70
61 const int width = notification_bitmap->width; 71 const int width = notification_bitmap->width;
62 const int height = notification_bitmap->height; 72 const int height = notification_bitmap->height;
63 73
64 if (width < 0 || height < 0 || width > max_width || height > max_height) 74 if (width < 0 || height < 0 || width > max_device_pixel_width ||
75 height > max_device_pixel_height)
65 return false; 76 return false;
66 77
67 // Ensure we have rgba data. 78 // Ensure we have rgba data.
68 std::string* rgba_data = notification_bitmap->data.get(); 79 std::string* rgba_data = notification_bitmap->data.get();
69 if (!rgba_data) 80 if (!rgba_data)
70 return false; 81 return false;
71 82
72 const size_t rgba_data_length = rgba_data->length(); 83 const size_t rgba_data_length = rgba_data->length();
73 const size_t rgba_area = width * height; 84 const size_t rgba_area = width * height;
74 85
(...skipping 18 matching lines...) Expand all
93 for (size_t t = 0; t < rgba_area; ++t) { 104 for (size_t t = 0; t < rgba_area; ++t) {
94 // |c_rgba_data| is RGBA, pixels is ARGB. 105 // |c_rgba_data| is RGBA, pixels is ARGB.
95 size_t rgba_index = t * BYTES_PER_PIXEL; 106 size_t rgba_index = t * BYTES_PER_PIXEL;
96 pixels[t] = SkPreMultiplyColor( 107 pixels[t] = SkPreMultiplyColor(
97 ((c_rgba_data[rgba_index + 3] & 0xFF) << 24) | 108 ((c_rgba_data[rgba_index + 3] & 0xFF) << 24) |
98 ((c_rgba_data[rgba_index + 0] & 0xFF) << 16) | 109 ((c_rgba_data[rgba_index + 0] & 0xFF) << 16) |
99 ((c_rgba_data[rgba_index + 1] & 0xFF) << 8) | 110 ((c_rgba_data[rgba_index + 1] & 0xFF) << 8) |
100 ((c_rgba_data[rgba_index + 2] & 0xFF) << 0)); 111 ((c_rgba_data[rgba_index + 2] & 0xFF) << 0));
101 } 112 }
102 113
103 // TODO(dewittj): Handle HiDPI images. 114 // TODO(dewittj): Handle HiDPI images with more than one scale factor
115 // representation.
104 gfx::ImageSkia skia(gfx::ImageSkiaRep(bitmap, 1.0f)); 116 gfx::ImageSkia skia(gfx::ImageSkiaRep(bitmap, 1.0f));
105 *return_image = gfx::Image(skia); 117 *return_image = gfx::Image(skia);
106 return true; 118 return true;
107 } 119 }
108 120
109 // Given an extension id and another id, returns an id that is unique 121 // Given an extension id and another id, returns an id that is unique
110 // relative to other extensions. 122 // relative to other extensions.
111 std::string CreateScopedIdentifier(const std::string& extension_id, 123 std::string CreateScopedIdentifier(const std::string& extension_id,
112 const std::string& id) { 124 const std::string& id) {
113 return extension_id + "-" + id; 125 return extension_id + "-" + id;
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 // First, make sure the required fields exist: type, title, message, icon. 249 // First, make sure the required fields exist: type, title, message, icon.
238 // These fields are defined as optional in IDL such that they can be used as 250 // These fields are defined as optional in IDL such that they can be used as
239 // optional for notification updates. But for notification creations, they 251 // optional for notification updates. But for notification creations, they
240 // should be present. 252 // should be present.
241 if (options->type == api::notifications::TEMPLATE_TYPE_NONE || 253 if (options->type == api::notifications::TEMPLATE_TYPE_NONE ||
242 !options->icon_url || !options->title || !options->message) { 254 !options->icon_url || !options->title || !options->message) {
243 SetError(kMissingRequiredPropertiesForCreateNotification); 255 SetError(kMissingRequiredPropertiesForCreateNotification);
244 return false; 256 return false;
245 } 257 }
246 258
259 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes();
260
261 float image_scale =
262 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back());
263
247 // Extract required fields: type, title, message, and icon. 264 // Extract required fields: type, title, message, and icon.
248 message_center::NotificationType type = 265 message_center::NotificationType type =
249 MapApiTemplateTypeToType(options->type); 266 MapApiTemplateTypeToType(options->type);
250 const base::string16 title(base::UTF8ToUTF16(*options->title)); 267 const base::string16 title(base::UTF8ToUTF16(*options->title));
251 const base::string16 message(base::UTF8ToUTF16(*options->message)); 268 const base::string16 message(base::UTF8ToUTF16(*options->message));
252 gfx::Image icon; 269 gfx::Image icon;
253 270
254 // TODO(dewittj): Return error if this fails. 271 if (!NotificationBitmapToGfxImage(image_scale,
255 NotificationBitmapToGfxImage(options->icon_bitmap.get(), &icon); 272 bitmap_sizes.icon_size,
273 options->icon_bitmap.get(),
274 &icon)) {
275 SetError(kUnableToDecodeIconError);
276 return false;
277 }
256 278
257 // Then, handle any optional data that's been provided. 279 // Then, handle any optional data that's been provided.
258 message_center::RichNotificationData optional_fields; 280 message_center::RichNotificationData optional_fields;
259 if (options->priority.get()) 281 if (options->priority.get())
260 optional_fields.priority = *options->priority; 282 optional_fields.priority = *options->priority;
261 283
262 if (options->event_time.get()) 284 if (options->event_time.get())
263 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time); 285 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time);
264 286
265 if (options->buttons.get()) { 287 if (options->buttons.get()) {
266 // Currently we allow up to 2 buttons. 288 // Currently we allow up to 2 buttons.
267 size_t number_of_buttons = options->buttons->size(); 289 size_t number_of_buttons = options->buttons->size();
268 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; 290 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons;
269 291
270 for (size_t i = 0; i < number_of_buttons; i++) { 292 for (size_t i = 0; i < number_of_buttons; i++) {
271 message_center::ButtonInfo info( 293 message_center::ButtonInfo info(
272 base::UTF8ToUTF16((*options->buttons)[i]->title)); 294 base::UTF8ToUTF16((*options->buttons)[i]->title));
273 NotificationBitmapToGfxImage((*options->buttons)[i]->icon_bitmap.get(), 295 NotificationBitmapToGfxImage(image_scale,
274 &info.icon); 296 bitmap_sizes.button_icon_size,
297 (*options->buttons)[i]->icon_bitmap.get(),
298 &info.icon);
275 optional_fields.buttons.push_back(info); 299 optional_fields.buttons.push_back(info);
276 } 300 }
277 } 301 }
278 302
279 if (options->context_message) { 303 if (options->context_message) {
280 optional_fields.context_message = 304 optional_fields.context_message =
281 base::UTF8ToUTF16(*options->context_message); 305 base::UTF8ToUTF16(*options->context_message);
282 } 306 }
283 307
284 bool has_image = NotificationBitmapToGfxImage(options->image_bitmap.get(), 308 bool has_image = NotificationBitmapToGfxImage(image_scale,
309 bitmap_sizes.image_size,
310 options->image_bitmap.get(),
285 &optional_fields.image); 311 &optional_fields.image);
286 // We should have an image if and only if the type is an image type. 312 // We should have an image if and only if the type is an image type.
287 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) 313 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) {
314 SetError(kExtraImageProvided);
288 return false; 315 return false;
316 }
289 317
290 // We should have list items if and only if the type is a multiple type. 318 // We should have list items if and only if the type is a multiple type.
291 bool has_list_items = options->items.get() && options->items->size() > 0; 319 bool has_list_items = options->items.get() && options->items->size() > 0;
292 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) 320 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) {
321 SetError(kExtraListItemsProvided);
293 return false; 322 return false;
323 }
294 324
295 if (options->progress.get() != NULL) { 325 if (options->progress.get() != NULL) {
296 // We should have progress if and only if the type is a progress type. 326 // We should have progress if and only if the type is a progress type.
297 if (type != message_center::NOTIFICATION_TYPE_PROGRESS) { 327 if (type != message_center::NOTIFICATION_TYPE_PROGRESS) {
298 SetError(kUnexpectedProgressValueForNonProgressType); 328 SetError(kUnexpectedProgressValueForNonProgressType);
299 return false; 329 return false;
300 } 330 }
301 optional_fields.progress = *options->progress; 331 optional_fields.progress = *options->progress;
302 // Progress value should range from 0 to 100. 332 // Progress value should range from 0 to 100.
303 if (optional_fields.progress < 0 || optional_fields.progress > 100) { 333 if (optional_fields.progress < 0 || optional_fields.progress > 100) {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 api_delegate); 368 api_delegate);
339 369
340 g_browser_process->notification_ui_manager()->Add(notification, GetProfile()); 370 g_browser_process->notification_ui_manager()->Add(notification, GetProfile());
341 return true; 371 return true;
342 } 372 }
343 373
344 bool NotificationsApiFunction::UpdateNotification( 374 bool NotificationsApiFunction::UpdateNotification(
345 const std::string& id, 375 const std::string& id,
346 api::notifications::NotificationOptions* options, 376 api::notifications::NotificationOptions* options,
347 Notification* notification) { 377 Notification* notification) {
378 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes();
379 float image_scale =
380 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back());
381
348 // Update optional fields if provided. 382 // Update optional fields if provided.
349 if (options->type != api::notifications::TEMPLATE_TYPE_NONE) 383 if (options->type != api::notifications::TEMPLATE_TYPE_NONE)
350 notification->set_type(MapApiTemplateTypeToType(options->type)); 384 notification->set_type(MapApiTemplateTypeToType(options->type));
351 if (options->title) 385 if (options->title)
352 notification->set_title(base::UTF8ToUTF16(*options->title)); 386 notification->set_title(base::UTF8ToUTF16(*options->title));
353 if (options->message) 387 if (options->message)
354 notification->set_message(base::UTF8ToUTF16(*options->message)); 388 notification->set_message(base::UTF8ToUTF16(*options->message));
355 389
356 // TODO(dewittj): Return error if this fails. 390 // TODO(dewittj): Return error if this fails.
357 if (options->icon_bitmap) { 391 if (options->icon_bitmap) {
358 gfx::Image icon; 392 gfx::Image icon;
359 NotificationBitmapToGfxImage(options->icon_bitmap.get(), &icon); 393 NotificationBitmapToGfxImage(
394 image_scale, bitmap_sizes.icon_size, options->icon_bitmap.get(), &icon);
360 notification->set_icon(icon); 395 notification->set_icon(icon);
361 } 396 }
362 397
363 if (options->priority) 398 if (options->priority)
364 notification->set_priority(*options->priority); 399 notification->set_priority(*options->priority);
365 400
366 if (options->event_time) 401 if (options->event_time)
367 notification->set_timestamp(base::Time::FromJsTime(*options->event_time)); 402 notification->set_timestamp(base::Time::FromJsTime(*options->event_time));
368 403
369 if (options->buttons) { 404 if (options->buttons) {
370 // Currently we allow up to 2 buttons. 405 // Currently we allow up to 2 buttons.
371 size_t number_of_buttons = options->buttons->size(); 406 size_t number_of_buttons = options->buttons->size();
372 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; 407 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons;
373 408
374 std::vector<message_center::ButtonInfo> buttons; 409 std::vector<message_center::ButtonInfo> buttons;
375 for (size_t i = 0; i < number_of_buttons; i++) { 410 for (size_t i = 0; i < number_of_buttons; i++) {
376 message_center::ButtonInfo button( 411 message_center::ButtonInfo button(
377 base::UTF8ToUTF16((*options->buttons)[i]->title)); 412 base::UTF8ToUTF16((*options->buttons)[i]->title));
378 NotificationBitmapToGfxImage((*options->buttons)[i]->icon_bitmap.get(), 413 NotificationBitmapToGfxImage(image_scale,
379 &button.icon); 414 bitmap_sizes.button_icon_size,
415 (*options->buttons)[i]->icon_bitmap.get(),
416 &button.icon);
380 buttons.push_back(button); 417 buttons.push_back(button);
381 } 418 }
382 notification->set_buttons(buttons); 419 notification->set_buttons(buttons);
383 } 420 }
384 421
385 if (options->context_message) { 422 if (options->context_message) {
386 notification->set_context_message( 423 notification->set_context_message(
387 base::UTF8ToUTF16(*options->context_message)); 424 base::UTF8ToUTF16(*options->context_message));
388 } 425 }
389 426
390 gfx::Image image; 427 gfx::Image image;
391 if (NotificationBitmapToGfxImage(options->image_bitmap.get(), &image)) { 428 bool has_image = NotificationBitmapToGfxImage(image_scale,
429 bitmap_sizes.image_size,
430 options->image_bitmap.get(),
431 &image);
432 if (has_image) {
392 // We should have an image if and only if the type is an image type. 433 // We should have an image if and only if the type is an image type.
393 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) 434 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) {
435 SetError(kExtraImageProvided);
394 return false; 436 return false;
437 }
395 notification->set_image(image); 438 notification->set_image(image);
396 } 439 }
397 440
398 if (options->progress) { 441 if (options->progress) {
399 // We should have progress if and only if the type is a progress type. 442 // We should have progress if and only if the type is a progress type.
400 if (notification->type() != message_center::NOTIFICATION_TYPE_PROGRESS) { 443 if (notification->type() != message_center::NOTIFICATION_TYPE_PROGRESS) {
401 SetError(kUnexpectedProgressValueForNonProgressType); 444 SetError(kUnexpectedProgressValueForNonProgressType);
402 return false; 445 return false;
403 } 446 }
404 int progress = *options->progress; 447 int progress = *options->progress;
405 // Progress value should range from 0 to 100. 448 // Progress value should range from 0 to 100.
406 if (progress < 0 || progress > 100) { 449 if (progress < 0 || progress > 100) {
407 SetError(kInvalidProgressValue); 450 SetError(kInvalidProgressValue);
408 return false; 451 return false;
409 } 452 }
410 notification->set_progress(progress); 453 notification->set_progress(progress);
411 } 454 }
412 455
413 if (options->items.get() && options->items->size() > 0) { 456 if (options->items.get() && options->items->size() > 0) {
414 // We should have list items if and only if the type is a multiple type. 457 // We should have list items if and only if the type is a multiple type.
415 if (notification->type() != message_center::NOTIFICATION_TYPE_MULTIPLE) 458 if (notification->type() != message_center::NOTIFICATION_TYPE_MULTIPLE) {
459 SetError(kExtraListItemsProvided);
416 return false; 460 return false;
461 }
417 462
418 std::vector<message_center::NotificationItem> items; 463 std::vector<message_center::NotificationItem> items;
419 using api::notifications::NotificationItem; 464 using api::notifications::NotificationItem;
420 std::vector<linked_ptr<NotificationItem> >::iterator i; 465 std::vector<linked_ptr<NotificationItem> >::iterator i;
421 for (i = options->items->begin(); i != options->items->end(); ++i) { 466 for (i = options->items->begin(); i != options->items->end(); ++i) {
422 message_center::NotificationItem item( 467 message_center::NotificationItem item(
423 base::UTF8ToUTF16(i->get()->title), 468 base::UTF8ToUTF16(i->get()->title),
424 base::UTF8ToUTF16(i->get()->message)); 469 base::UTF8ToUTF16(i->get()->message));
425 items.push_back(item); 470 items.push_back(item);
426 } 471 }
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
614 ? api::notifications::PERMISSION_LEVEL_GRANTED 659 ? api::notifications::PERMISSION_LEVEL_GRANTED
615 : api::notifications::PERMISSION_LEVEL_DENIED; 660 : api::notifications::PERMISSION_LEVEL_DENIED;
616 661
617 SetResult(new base::StringValue(api::notifications::ToString(result))); 662 SetResult(new base::StringValue(api::notifications::ToString(result)));
618 SendResponse(true); 663 SendResponse(true);
619 664
620 return true; 665 return true;
621 } 666 }
622 667
623 } // namespace extensions 668 } // namespace extensions
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/extensions/api/notifications/notifications_apitest.cc » ('j') | chrome/unit_tests.isolate » ('J')

Powered by Google App Engine
This is Rietveld 408576698