| OLD | NEW |
| 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" |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 46 "Unable to successfully use the provided image."; | 46 "Unable to successfully use the provided image."; |
| 47 const char kUnexpectedProgressValueForNonProgressType[] = | 47 const char kUnexpectedProgressValueForNonProgressType[] = |
| 48 "The progress value should not be specified for non-progress notification"; | 48 "The progress value should not be specified for non-progress notification"; |
| 49 const char kInvalidProgressValue[] = | 49 const char kInvalidProgressValue[] = |
| 50 "The progress value should range from 0 to 100"; | 50 "The progress value should range from 0 to 100"; |
| 51 const char kExtraListItemsProvided[] = | 51 const char kExtraListItemsProvided[] = |
| 52 "List items provided for notification type != list"; | 52 "List items provided for notification type != list"; |
| 53 const char kExtraImageProvided[] = | 53 const char kExtraImageProvided[] = |
| 54 "Image resource provided for notification type != image"; | 54 "Image resource provided for notification type != image"; |
| 55 | 55 |
| 56 // Converts an object with width, height, and data in RGBA format into an | 56 typedef std::vector<linked_ptr<notifications::ImageRepresentation> > |
| 57 // gfx::Image (in ARGB format). | 57 ImageRepresentationList; |
| 58 bool NotificationBitmapToGfxImage( | 58 |
| 59 float max_scale, | 59 bool ImageRepresentationToImageSkiaRep(float max_scale, |
| 60 const gfx::Size& target_size_dips, | 60 const gfx::Size& target_size_dips, |
| 61 api::notifications::NotificationBitmap* notification_bitmap, | 61 notifications::BitmapData* bitmap_data, |
| 62 gfx::Image* return_image) { | 62 float configured_scale, |
| 63 if (!notification_bitmap) | 63 gfx::ImageSkiaRep* return_rep) { |
| 64 if (!bitmap_data || !return_rep) { |
| 65 LOG(WARNING) << "Bitmap data: " << bitmap_data; |
| 66 LOG(WARNING) << "return_rep: " << return_rep; |
| 67 |
| 64 return false; | 68 return false; |
| 69 } |
| 65 | 70 |
| 66 const int max_device_pixel_width = target_size_dips.width() * max_scale; | 71 const int max_device_pixel_width = target_size_dips.width() * max_scale; |
| 67 const int max_device_pixel_height = target_size_dips.height() * max_scale; | 72 const int max_device_pixel_height = target_size_dips.height() * max_scale; |
| 68 | 73 |
| 69 const int BYTES_PER_PIXEL = 4; | 74 const int BYTES_PER_PIXEL = 4; |
| 70 | 75 |
| 71 const int width = notification_bitmap->width; | 76 const int width = bitmap_data->width; |
| 72 const int height = notification_bitmap->height; | 77 const int height = bitmap_data->height; |
| 73 | 78 |
| 74 if (width < 0 || height < 0 || width > max_device_pixel_width || | 79 if (configured_scale < 1.0f || width < 0 || height < 0 || |
| 75 height > max_device_pixel_height) | 80 width > max_device_pixel_width || height > max_device_pixel_height) { |
| 81 LOG(WARNING) << "Configured scale: " << configured_scale; |
| 82 LOG(WARNING) << "width: " << width; |
| 83 LOG(WARNING) << "height: " << height; |
| 84 return false; |
| 85 } |
| 86 |
| 87 // Ensure we have rgba data. |
| 88 if (!bitmap_data->data.length()) |
| 76 return false; | 89 return false; |
| 77 | 90 |
| 78 // Ensure we have rgba data. | 91 const size_t rgba_data_length = bitmap_data->data.length(); |
| 79 std::string* rgba_data = notification_bitmap->data.get(); | |
| 80 if (!rgba_data) | |
| 81 return false; | |
| 82 | |
| 83 const size_t rgba_data_length = rgba_data->length(); | |
| 84 const size_t rgba_area = width * height; | 92 const size_t rgba_area = width * height; |
| 85 | 93 |
| 86 if (rgba_data_length != rgba_area * BYTES_PER_PIXEL) | 94 if (rgba_data_length != rgba_area * BYTES_PER_PIXEL) |
| 87 return false; | 95 return false; |
| 88 | 96 |
| 89 // Now configure the bitmap with the sanitized dimensions. | 97 // Now configure the bitmap with the sanitized dimensions. |
| 90 SkBitmap bitmap; | 98 SkBitmap bitmap; |
| 91 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); | 99 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); |
| 92 | 100 |
| 93 // Allocate the actual backing store. | 101 // Allocate the actual backing store. |
| 94 if (!bitmap.allocPixels()) | 102 if (!bitmap.allocPixels()) |
| 95 return false; | 103 return false; |
| 96 | 104 |
| 97 // Ensure that our bitmap and our data now refer to the same number of pixels. | 105 // Ensure that our bitmap and our data now refer to the same number of pixels. |
| 98 if (rgba_data_length != bitmap.getSafeSize()) | 106 if (rgba_data_length != bitmap.getSafeSize()) |
| 99 return false; | 107 return false; |
| 100 | 108 |
| 101 uint32_t* pixels = bitmap.getAddr32(0, 0); | 109 uint32_t* pixels = bitmap.getAddr32(0, 0); |
| 102 const char* c_rgba_data = rgba_data->data(); | 110 const char* c_rgba_data = bitmap_data->data.data(); |
| 103 | 111 |
| 104 for (size_t t = 0; t < rgba_area; ++t) { | 112 for (size_t t = 0; t < rgba_area; ++t) { |
| 105 // |c_rgba_data| is RGBA, pixels is ARGB. | 113 // |c_rgba_data| is RGBA, pixels is ARGB. |
| 106 size_t rgba_index = t * BYTES_PER_PIXEL; | 114 size_t rgba_index = t * BYTES_PER_PIXEL; |
| 107 pixels[t] = SkPreMultiplyColor( | 115 pixels[t] = SkPreMultiplyColor( |
| 108 ((c_rgba_data[rgba_index + 3] & 0xFF) << 24) | | 116 ((c_rgba_data[rgba_index + 3] & 0xFF) << 24) | |
| 109 ((c_rgba_data[rgba_index + 0] & 0xFF) << 16) | | 117 ((c_rgba_data[rgba_index + 0] & 0xFF) << 16) | |
| 110 ((c_rgba_data[rgba_index + 1] & 0xFF) << 8) | | 118 ((c_rgba_data[rgba_index + 1] & 0xFF) << 8) | |
| 111 ((c_rgba_data[rgba_index + 2] & 0xFF) << 0)); | 119 ((c_rgba_data[rgba_index + 2] & 0xFF) << 0)); |
| 112 } | 120 } |
| 113 | 121 |
| 114 // TODO(dewittj): Handle HiDPI images with more than one scale factor | 122 // TODO(dewittj): Handle HiDPI images with more than one scale factor |
| 115 // representation. | 123 // representation. |
| 116 gfx::ImageSkia skia(gfx::ImageSkiaRep(bitmap, 1.0f)); | 124 *return_rep = gfx::ImageSkiaRep(bitmap, configured_scale); |
| 117 *return_image = gfx::Image(skia); | |
| 118 return true; | 125 return true; |
| 119 } | 126 } |
| 120 | 127 |
| 128 // Converts an object with width, height, and data in RGBA format into an |
| 129 // gfx::Image (in ARGB format). |
| 130 bool ImageRepresentationListToGfxImage(float max_scale, |
| 131 const gfx::Size& target_size_dips, |
| 132 ImageRepresentationList* bitmap_list, |
| 133 gfx::Image* return_image) { |
| 134 if (!bitmap_list || bitmap_list->size() == 0) |
| 135 return false; |
| 136 |
| 137 gfx::ImageSkia out; |
| 138 ImageRepresentationList::iterator iter = bitmap_list->begin(); |
| 139 while (iter != bitmap_list->end()) { |
| 140 notifications::ImageRepresentation* rep = (*iter).get(); |
| 141 if (rep->scale > max_scale) |
| 142 return false; |
| 143 |
| 144 notifications::BitmapData* bitmap = rep->data.as_bitmap_data.get(); |
| 145 gfx::ImageSkiaRep skia_rep; |
| 146 if (!ImageRepresentationToImageSkiaRep( |
| 147 max_scale, target_size_dips, bitmap, rep->scale, &skia_rep)) |
| 148 return false; |
| 149 |
| 150 out.AddRepresentation(skia_rep); |
| 151 |
| 152 ++iter; |
| 153 } |
| 154 *return_image = gfx::Image(out); |
| 155 return true; |
| 156 } |
| 157 |
| 121 // Given an extension id and another id, returns an id that is unique | 158 // Given an extension id and another id, returns an id that is unique |
| 122 // relative to other extensions. | 159 // relative to other extensions. |
| 123 std::string CreateScopedIdentifier(const std::string& extension_id, | 160 std::string CreateScopedIdentifier(const std::string& extension_id, |
| 124 const std::string& id) { | 161 const std::string& id) { |
| 125 return extension_id + "-" + id; | 162 return extension_id + "-" + id; |
| 126 } | 163 } |
| 127 | 164 |
| 128 // Removes the unique internal identifier to send the ID as the | 165 // Removes the unique internal identifier to send the ID as the |
| 129 // extension expects it. | 166 // extension expects it. |
| 130 std::string StripScopeFromIdentifier(const std::string& extension_id, | 167 std::string StripScopeFromIdentifier(const std::string& extension_id, |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 NotificationsApiFunction::~NotificationsApiFunction() { | 290 NotificationsApiFunction::~NotificationsApiFunction() { |
| 254 } | 291 } |
| 255 | 292 |
| 256 bool NotificationsApiFunction::CreateNotification( | 293 bool NotificationsApiFunction::CreateNotification( |
| 257 const std::string& id, | 294 const std::string& id, |
| 258 api::notifications::NotificationOptions* options) { | 295 api::notifications::NotificationOptions* options) { |
| 259 // First, make sure the required fields exist: type, title, message, icon. | 296 // First, make sure the required fields exist: type, title, message, icon. |
| 260 // These fields are defined as optional in IDL such that they can be used as | 297 // These fields are defined as optional in IDL such that they can be used as |
| 261 // optional for notification updates. But for notification creations, they | 298 // optional for notification updates. But for notification creations, they |
| 262 // should be present. | 299 // should be present. |
| 263 if (options->type == api::notifications::TEMPLATE_TYPE_NONE || | 300 if (options->icons == NULL || options->icons->size() < 1) { |
| 264 !options->icon_url || !options->title || !options->message) { | |
| 265 SetError(kMissingRequiredPropertiesForCreateNotification); | 301 SetError(kMissingRequiredPropertiesForCreateNotification); |
| 266 return false; | 302 return false; |
| 267 } | 303 } |
| 304 if (options->type == api::notifications::TEMPLATE_TYPE_NONE || |
| 305 !options->title || !options->message) { |
| 306 SetError(kMissingRequiredPropertiesForCreateNotification); |
| 307 return false; |
| 308 } |
| 268 | 309 |
| 269 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes(); | 310 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes(); |
| 270 | 311 |
| 271 float image_scale = | 312 float image_scale = |
| 272 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back()); | 313 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back()); |
| 273 | 314 |
| 274 // Extract required fields: type, title, message, and icon. | 315 // Extract required fields: type, title, message, and icon. |
| 275 message_center::NotificationType type = | 316 message_center::NotificationType type = |
| 276 MapApiTemplateTypeToType(options->type); | 317 MapApiTemplateTypeToType(options->type); |
| 277 const base::string16 title(base::UTF8ToUTF16(*options->title)); | 318 const base::string16 title(base::UTF8ToUTF16(*options->title)); |
| 278 const base::string16 message(base::UTF8ToUTF16(*options->message)); | 319 const base::string16 message(base::UTF8ToUTF16(*options->message)); |
| 279 gfx::Image icon; | 320 gfx::Image icon; |
| 280 | 321 |
| 281 if (!NotificationBitmapToGfxImage(image_scale, | 322 if (!ImageRepresentationListToGfxImage( |
| 282 bitmap_sizes.icon_size, | 323 image_scale, bitmap_sizes.icon_size, options->icons.get(), &icon)) { |
| 283 options->icon_bitmap.get(), | |
| 284 &icon)) { | |
| 285 SetError(kUnableToDecodeIconError); | 324 SetError(kUnableToDecodeIconError); |
| 286 return false; | 325 return false; |
| 287 } | 326 } |
| 288 | 327 |
| 289 // Then, handle any optional data that's been provided. | 328 // Then, handle any optional data that's been provided. |
| 290 message_center::RichNotificationData optional_fields; | 329 message_center::RichNotificationData optional_fields; |
| 291 if (options->priority.get()) | 330 if (options->priority.get()) |
| 292 optional_fields.priority = *options->priority; | 331 optional_fields.priority = *options->priority; |
| 293 | 332 |
| 294 if (options->event_time.get()) | 333 if (options->event_time.get()) |
| 295 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time); | 334 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time); |
| 296 | 335 |
| 297 if (options->buttons.get()) { | 336 if (options->buttons.get()) { |
| 298 // Currently we allow up to 2 buttons. | 337 // Currently we allow up to 2 buttons. |
| 299 size_t number_of_buttons = options->buttons->size(); | 338 size_t number_of_buttons = options->buttons->size(); |
| 300 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; | 339 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; |
| 301 | 340 |
| 302 for (size_t i = 0; i < number_of_buttons; i++) { | 341 for (size_t i = 0; i < number_of_buttons; i++) { |
| 303 message_center::ButtonInfo info( | 342 message_center::ButtonInfo info( |
| 304 base::UTF8ToUTF16((*options->buttons)[i]->title)); | 343 base::UTF8ToUTF16((*options->buttons)[i]->title)); |
| 305 NotificationBitmapToGfxImage(image_scale, | 344 ImageRepresentationListToGfxImage(image_scale, |
| 306 bitmap_sizes.button_icon_size, | 345 bitmap_sizes.button_icon_size, |
| 307 (*options->buttons)[i]->icon_bitmap.get(), | 346 (*options->buttons)[i]->icons.get(), |
| 308 &info.icon); | 347 &info.icon); |
| 309 optional_fields.buttons.push_back(info); | 348 optional_fields.buttons.push_back(info); |
| 310 } | 349 } |
| 311 } | 350 } |
| 312 | 351 |
| 313 if (options->context_message) { | 352 if (options->context_message) { |
| 314 optional_fields.context_message = | 353 optional_fields.context_message = |
| 315 base::UTF8ToUTF16(*options->context_message); | 354 base::UTF8ToUTF16(*options->context_message); |
| 316 } | 355 } |
| 317 | 356 |
| 318 bool has_image = NotificationBitmapToGfxImage(image_scale, | 357 bool has_image = ImageRepresentationListToGfxImage(image_scale, |
| 319 bitmap_sizes.image_size, | 358 bitmap_sizes.image_size, |
| 320 options->image_bitmap.get(), | 359 options->images.get(), |
| 321 &optional_fields.image); | 360 &optional_fields.image); |
| 322 // We should have an image if and only if the type is an image type. | 361 // We should have an image if and only if the type is an image type. |
| 323 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) { | 362 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) { |
| 324 SetError(kExtraImageProvided); | 363 SetError(kExtraImageProvided); |
| 325 return false; | 364 return false; |
| 326 } | 365 } |
| 327 | 366 |
| 328 // We should have list items if and only if the type is a multiple type. | 367 // We should have list items if and only if the type is a multiple type. |
| 329 bool has_list_items = options->items.get() && options->items->size() > 0; | 368 bool has_list_items = options->items.get() && options->items->size() > 0; |
| 330 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) { | 369 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) { |
| 331 SetError(kExtraListItemsProvided); | 370 SetError(kExtraListItemsProvided); |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 | 430 |
| 392 // Update optional fields if provided. | 431 // Update optional fields if provided. |
| 393 if (options->type != api::notifications::TEMPLATE_TYPE_NONE) | 432 if (options->type != api::notifications::TEMPLATE_TYPE_NONE) |
| 394 notification->set_type(MapApiTemplateTypeToType(options->type)); | 433 notification->set_type(MapApiTemplateTypeToType(options->type)); |
| 395 if (options->title) | 434 if (options->title) |
| 396 notification->set_title(base::UTF8ToUTF16(*options->title)); | 435 notification->set_title(base::UTF8ToUTF16(*options->title)); |
| 397 if (options->message) | 436 if (options->message) |
| 398 notification->set_message(base::UTF8ToUTF16(*options->message)); | 437 notification->set_message(base::UTF8ToUTF16(*options->message)); |
| 399 | 438 |
| 400 // TODO(dewittj): Return error if this fails. | 439 // TODO(dewittj): Return error if this fails. |
| 401 if (options->icon_bitmap) { | 440 if (options->icons) { |
| 402 gfx::Image icon; | 441 gfx::Image icon; |
| 403 NotificationBitmapToGfxImage( | 442 ImageRepresentationListToGfxImage( |
| 404 image_scale, bitmap_sizes.icon_size, options->icon_bitmap.get(), &icon); | 443 image_scale, bitmap_sizes.icon_size, options->icons.get(), &icon); |
| 405 notification->set_icon(icon); | 444 notification->set_icon(icon); |
| 406 } | 445 } |
| 407 | 446 |
| 408 if (options->priority) | 447 if (options->priority) |
| 409 notification->set_priority(*options->priority); | 448 notification->set_priority(*options->priority); |
| 410 | 449 |
| 411 if (options->event_time) | 450 if (options->event_time) |
| 412 notification->set_timestamp(base::Time::FromJsTime(*options->event_time)); | 451 notification->set_timestamp(base::Time::FromJsTime(*options->event_time)); |
| 413 | 452 |
| 414 if (options->buttons) { | 453 if (options->buttons) { |
| 415 // Currently we allow up to 2 buttons. | 454 // Currently we allow up to 2 buttons. |
| 416 size_t number_of_buttons = options->buttons->size(); | 455 size_t number_of_buttons = options->buttons->size(); |
| 417 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; | 456 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; |
| 418 | 457 |
| 419 std::vector<message_center::ButtonInfo> buttons; | 458 std::vector<message_center::ButtonInfo> buttons; |
| 420 for (size_t i = 0; i < number_of_buttons; i++) { | 459 for (size_t i = 0; i < number_of_buttons; i++) { |
| 421 message_center::ButtonInfo button( | 460 message_center::ButtonInfo button( |
| 422 base::UTF8ToUTF16((*options->buttons)[i]->title)); | 461 base::UTF8ToUTF16((*options->buttons)[i]->title)); |
| 423 NotificationBitmapToGfxImage(image_scale, | 462 ImageRepresentationListToGfxImage(image_scale, |
| 424 bitmap_sizes.button_icon_size, | 463 bitmap_sizes.button_icon_size, |
| 425 (*options->buttons)[i]->icon_bitmap.get(), | 464 (*options->buttons)[i]->icons.get(), |
| 426 &button.icon); | 465 &button.icon); |
| 427 buttons.push_back(button); | 466 buttons.push_back(button); |
| 428 } | 467 } |
| 429 notification->set_buttons(buttons); | 468 notification->set_buttons(buttons); |
| 430 } | 469 } |
| 431 | 470 |
| 432 if (options->context_message) { | 471 if (options->context_message) { |
| 433 notification->set_context_message( | 472 notification->set_context_message( |
| 434 base::UTF8ToUTF16(*options->context_message)); | 473 base::UTF8ToUTF16(*options->context_message)); |
| 435 } | 474 } |
| 436 | 475 |
| 437 gfx::Image image; | 476 gfx::Image image; |
| 438 bool has_image = NotificationBitmapToGfxImage(image_scale, | 477 bool has_image = ImageRepresentationListToGfxImage( |
| 439 bitmap_sizes.image_size, | 478 image_scale, bitmap_sizes.image_size, options->images.get(), &image); |
| 440 options->image_bitmap.get(), | |
| 441 &image); | |
| 442 if (has_image) { | 479 if (has_image) { |
| 443 // We should have an image if and only if the type is an image type. | 480 // We should have an image if and only if the type is an image type. |
| 444 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) { | 481 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) { |
| 445 SetError(kExtraImageProvided); | 482 SetError(kExtraImageProvided); |
| 446 return false; | 483 return false; |
| 447 } | 484 } |
| 448 notification->set_image(image); | 485 notification->set_image(image); |
| 449 } | 486 } |
| 450 | 487 |
| 451 if (options->progress) { | 488 if (options->progress) { |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 669 ? api::notifications::PERMISSION_LEVEL_GRANTED | 706 ? api::notifications::PERMISSION_LEVEL_GRANTED |
| 670 : api::notifications::PERMISSION_LEVEL_DENIED; | 707 : api::notifications::PERMISSION_LEVEL_DENIED; |
| 671 | 708 |
| 672 SetResult(new base::StringValue(api::notifications::ToString(result))); | 709 SetResult(new base::StringValue(api::notifications::ToString(result))); |
| 673 SendResponse(true); | 710 SendResponse(true); |
| 674 | 711 |
| 675 return true; | 712 return true; |
| 676 } | 713 } |
| 677 | 714 |
| 678 } // namespace extensions | 715 } // namespace extensions |
| OLD | NEW |