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" |
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_conversion_helper.h" |
17 #include "chrome/browser/notifications/notification_ui_manager.h" | 18 #include "chrome/browser/notifications/notification_ui_manager.h" |
18 #include "chrome/browser/profiles/profile.h" | 19 #include "chrome/browser/profiles/profile.h" |
19 #include "chrome/common/chrome_version_info.h" | 20 #include "chrome/common/chrome_version_info.h" |
20 #include "chrome/common/extensions/api/notifications/notification_style.h" | 21 #include "chrome/common/extensions/api/notifications/notification_style.h" |
21 #include "content/public/browser/render_process_host.h" | 22 #include "content/public/browser/render_process_host.h" |
22 #include "content/public/browser/render_view_host.h" | 23 #include "content/public/browser/render_view_host.h" |
23 #include "content/public/browser/web_contents.h" | 24 #include "content/public/browser/web_contents.h" |
24 #include "extensions/browser/event_router.h" | 25 #include "extensions/browser/event_router.h" |
25 #include "extensions/common/extension.h" | 26 #include "extensions/common/extension.h" |
26 #include "extensions/common/features/feature.h" | 27 #include "extensions/common/features/feature.h" |
(...skipping 19 matching lines...) Expand all Loading... |
46 "Unable to successfully use the provided image."; | 47 "Unable to successfully use the provided image."; |
47 const char kUnexpectedProgressValueForNonProgressType[] = | 48 const char kUnexpectedProgressValueForNonProgressType[] = |
48 "The progress value should not be specified for non-progress notification"; | 49 "The progress value should not be specified for non-progress notification"; |
49 const char kInvalidProgressValue[] = | 50 const char kInvalidProgressValue[] = |
50 "The progress value should range from 0 to 100"; | 51 "The progress value should range from 0 to 100"; |
51 const char kExtraListItemsProvided[] = | 52 const char kExtraListItemsProvided[] = |
52 "List items provided for notification type != list"; | 53 "List items provided for notification type != list"; |
53 const char kExtraImageProvided[] = | 54 const char kExtraImageProvided[] = |
54 "Image resource provided for notification type != image"; | 55 "Image resource provided for notification type != image"; |
55 | 56 |
56 // Converts an object with width, height, and data in RGBA format into an | |
57 // gfx::Image (in ARGB format). | |
58 bool NotificationBitmapToGfxImage( | |
59 float max_scale, | |
60 const gfx::Size& target_size_dips, | |
61 api::notifications::NotificationBitmap* notification_bitmap, | |
62 gfx::Image* return_image) { | |
63 if (!notification_bitmap) | |
64 return false; | |
65 | |
66 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; | |
68 | |
69 const int BYTES_PER_PIXEL = 4; | |
70 | |
71 const int width = notification_bitmap->width; | |
72 const int height = notification_bitmap->height; | |
73 | |
74 if (width < 0 || height < 0 || width > max_device_pixel_width || | |
75 height > max_device_pixel_height) | |
76 return false; | |
77 | |
78 // Ensure we have rgba data. | |
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; | |
85 | |
86 if (rgba_data_length != rgba_area * BYTES_PER_PIXEL) | |
87 return false; | |
88 | |
89 SkBitmap bitmap; | |
90 // Allocate the actual backing store with the sanitized dimensions. | |
91 if (!bitmap.allocN32Pixels(width, height)) | |
92 return false; | |
93 | |
94 // Ensure that our bitmap and our data now refer to the same number of pixels. | |
95 if (rgba_data_length != bitmap.getSafeSize()) | |
96 return false; | |
97 | |
98 uint32_t* pixels = bitmap.getAddr32(0, 0); | |
99 const char* c_rgba_data = rgba_data->data(); | |
100 | |
101 for (size_t t = 0; t < rgba_area; ++t) { | |
102 // |c_rgba_data| is RGBA, pixels is ARGB. | |
103 size_t rgba_index = t * BYTES_PER_PIXEL; | |
104 pixels[t] = SkPreMultiplyColor( | |
105 ((c_rgba_data[rgba_index + 3] & 0xFF) << 24) | | |
106 ((c_rgba_data[rgba_index + 0] & 0xFF) << 16) | | |
107 ((c_rgba_data[rgba_index + 1] & 0xFF) << 8) | | |
108 ((c_rgba_data[rgba_index + 2] & 0xFF) << 0)); | |
109 } | |
110 | |
111 // TODO(dewittj): Handle HiDPI images with more than one scale factor | |
112 // representation. | |
113 gfx::ImageSkia skia(gfx::ImageSkiaRep(bitmap, 1.0f)); | |
114 *return_image = gfx::Image(skia); | |
115 return true; | |
116 } | |
117 | |
118 // Given an extension id and another id, returns an id that is unique | 57 // Given an extension id and another id, returns an id that is unique |
119 // relative to other extensions. | 58 // relative to other extensions. |
120 std::string CreateScopedIdentifier(const std::string& extension_id, | 59 std::string CreateScopedIdentifier(const std::string& extension_id, |
121 const std::string& id) { | 60 const std::string& id) { |
122 return extension_id + "-" + id; | 61 return extension_id + "-" + id; |
123 } | 62 } |
124 | 63 |
125 // Removes the unique internal identifier to send the ID as the | 64 // Removes the unique internal identifier to send the ID as the |
126 // extension expects it. | 65 // extension expects it. |
127 std::string StripScopeFromIdentifier(const std::string& extension_id, | 66 std::string StripScopeFromIdentifier(const std::string& extension_id, |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 float image_scale = | 207 float image_scale = |
269 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back()); | 208 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back()); |
270 | 209 |
271 // Extract required fields: type, title, message, and icon. | 210 // Extract required fields: type, title, message, and icon. |
272 message_center::NotificationType type = | 211 message_center::NotificationType type = |
273 MapApiTemplateTypeToType(options->type); | 212 MapApiTemplateTypeToType(options->type); |
274 const base::string16 title(base::UTF8ToUTF16(*options->title)); | 213 const base::string16 title(base::UTF8ToUTF16(*options->title)); |
275 const base::string16 message(base::UTF8ToUTF16(*options->message)); | 214 const base::string16 message(base::UTF8ToUTF16(*options->message)); |
276 gfx::Image icon; | 215 gfx::Image icon; |
277 | 216 |
278 if (!NotificationBitmapToGfxImage(image_scale, | 217 if (!NotificationConversionHelper::NotificationBitmapToGfxImage( |
279 bitmap_sizes.icon_size, | 218 image_scale, |
280 options->icon_bitmap.get(), | 219 bitmap_sizes.icon_size, |
281 &icon)) { | 220 options->icon_bitmap.get(), |
| 221 &icon)) { |
282 SetError(kUnableToDecodeIconError); | 222 SetError(kUnableToDecodeIconError); |
283 return false; | 223 return false; |
284 } | 224 } |
285 | 225 |
286 // Then, handle any optional data that's been provided. | 226 // Then, handle any optional data that's been provided. |
287 message_center::RichNotificationData optional_fields; | 227 message_center::RichNotificationData optional_fields; |
288 if (options->app_icon_mask_url.get()) { | 228 if (options->app_icon_mask_url.get()) { |
289 if (!NotificationBitmapToGfxImage(image_scale, | 229 if (!NotificationConversionHelper::NotificationBitmapToGfxImage( |
290 bitmap_sizes.app_icon_mask_size, | 230 image_scale, |
291 options->app_icon_mask_bitmap.get(), | 231 bitmap_sizes.app_icon_mask_size, |
292 &optional_fields.small_image)) { | 232 options->app_icon_mask_bitmap.get(), |
| 233 &optional_fields.small_image)) { |
293 SetError(kUnableToDecodeIconError); | 234 SetError(kUnableToDecodeIconError); |
294 return false; | 235 return false; |
295 } | 236 } |
296 } | 237 } |
297 | 238 |
298 if (options->priority.get()) | 239 if (options->priority.get()) |
299 optional_fields.priority = *options->priority; | 240 optional_fields.priority = *options->priority; |
300 | 241 |
301 if (options->event_time.get()) | 242 if (options->event_time.get()) |
302 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time); | 243 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time); |
303 | 244 |
304 if (options->buttons.get()) { | 245 if (options->buttons.get()) { |
305 // Currently we allow up to 2 buttons. | 246 // Currently we allow up to 2 buttons. |
306 size_t number_of_buttons = options->buttons->size(); | 247 size_t number_of_buttons = options->buttons->size(); |
307 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; | 248 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; |
308 | 249 |
309 for (size_t i = 0; i < number_of_buttons; i++) { | 250 for (size_t i = 0; i < number_of_buttons; i++) { |
310 message_center::ButtonInfo info( | 251 message_center::ButtonInfo info( |
311 base::UTF8ToUTF16((*options->buttons)[i]->title)); | 252 base::UTF8ToUTF16((*options->buttons)[i]->title)); |
312 NotificationBitmapToGfxImage(image_scale, | 253 NotificationConversionHelper::NotificationBitmapToGfxImage( |
313 bitmap_sizes.button_icon_size, | 254 image_scale, |
314 (*options->buttons)[i]->icon_bitmap.get(), | 255 bitmap_sizes.button_icon_size, |
315 &info.icon); | 256 (*options->buttons)[i]->icon_bitmap.get(), |
| 257 &info.icon); |
316 optional_fields.buttons.push_back(info); | 258 optional_fields.buttons.push_back(info); |
317 } | 259 } |
318 } | 260 } |
319 | 261 |
320 if (options->context_message) { | 262 if (options->context_message) { |
321 optional_fields.context_message = | 263 optional_fields.context_message = |
322 base::UTF8ToUTF16(*options->context_message); | 264 base::UTF8ToUTF16(*options->context_message); |
323 } | 265 } |
324 | 266 |
325 bool has_image = NotificationBitmapToGfxImage(image_scale, | 267 bool has_image = NotificationConversionHelper::NotificationBitmapToGfxImage( |
326 bitmap_sizes.image_size, | 268 image_scale, |
327 options->image_bitmap.get(), | 269 bitmap_sizes.image_size, |
328 &optional_fields.image); | 270 options->image_bitmap.get(), |
| 271 &optional_fields.image); |
329 // We should have an image if and only if the type is an image type. | 272 // We should have an image if and only if the type is an image type. |
330 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) { | 273 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) { |
331 SetError(kExtraImageProvided); | 274 SetError(kExtraImageProvided); |
332 return false; | 275 return false; |
333 } | 276 } |
334 | 277 |
335 // We should have list items if and only if the type is a multiple type. | 278 // We should have list items if and only if the type is a multiple type. |
336 bool has_list_items = options->items.get() && options->items->size() > 0; | 279 bool has_list_items = options->items.get() && options->items->size() > 0; |
337 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) { | 280 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) { |
338 SetError(kExtraListItemsProvided); | 281 SetError(kExtraListItemsProvided); |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 if (options->type != api::notifications::TEMPLATE_TYPE_NONE) | 343 if (options->type != api::notifications::TEMPLATE_TYPE_NONE) |
401 notification->set_type(MapApiTemplateTypeToType(options->type)); | 344 notification->set_type(MapApiTemplateTypeToType(options->type)); |
402 if (options->title) | 345 if (options->title) |
403 notification->set_title(base::UTF8ToUTF16(*options->title)); | 346 notification->set_title(base::UTF8ToUTF16(*options->title)); |
404 if (options->message) | 347 if (options->message) |
405 notification->set_message(base::UTF8ToUTF16(*options->message)); | 348 notification->set_message(base::UTF8ToUTF16(*options->message)); |
406 | 349 |
407 // TODO(dewittj): Return error if this fails. | 350 // TODO(dewittj): Return error if this fails. |
408 if (options->icon_bitmap) { | 351 if (options->icon_bitmap) { |
409 gfx::Image icon; | 352 gfx::Image icon; |
410 NotificationBitmapToGfxImage( | 353 NotificationConversionHelper::NotificationBitmapToGfxImage( |
411 image_scale, bitmap_sizes.icon_size, options->icon_bitmap.get(), &icon); | 354 image_scale, bitmap_sizes.icon_size, options->icon_bitmap.get(), &icon); |
412 notification->set_icon(icon); | 355 notification->set_icon(icon); |
413 } | 356 } |
414 | 357 |
415 gfx::Image app_icon_mask; | 358 gfx::Image app_icon_mask; |
416 if (NotificationBitmapToGfxImage(image_scale, | 359 if (NotificationConversionHelper::NotificationBitmapToGfxImage( |
417 bitmap_sizes.app_icon_mask_size, | 360 image_scale, |
418 options->app_icon_mask_bitmap.get(), | 361 bitmap_sizes.app_icon_mask_size, |
419 &app_icon_mask)) { | 362 options->app_icon_mask_bitmap.get(), |
| 363 &app_icon_mask)) { |
420 notification->set_small_image(app_icon_mask); | 364 notification->set_small_image(app_icon_mask); |
421 } | 365 } |
422 | 366 |
423 if (options->priority) | 367 if (options->priority) |
424 notification->set_priority(*options->priority); | 368 notification->set_priority(*options->priority); |
425 | 369 |
426 if (options->event_time) | 370 if (options->event_time) |
427 notification->set_timestamp(base::Time::FromJsTime(*options->event_time)); | 371 notification->set_timestamp(base::Time::FromJsTime(*options->event_time)); |
428 | 372 |
429 if (options->buttons) { | 373 if (options->buttons) { |
430 // Currently we allow up to 2 buttons. | 374 // Currently we allow up to 2 buttons. |
431 size_t number_of_buttons = options->buttons->size(); | 375 size_t number_of_buttons = options->buttons->size(); |
432 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; | 376 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; |
433 | 377 |
434 std::vector<message_center::ButtonInfo> buttons; | 378 std::vector<message_center::ButtonInfo> buttons; |
435 for (size_t i = 0; i < number_of_buttons; i++) { | 379 for (size_t i = 0; i < number_of_buttons; i++) { |
436 message_center::ButtonInfo button( | 380 message_center::ButtonInfo button( |
437 base::UTF8ToUTF16((*options->buttons)[i]->title)); | 381 base::UTF8ToUTF16((*options->buttons)[i]->title)); |
438 NotificationBitmapToGfxImage(image_scale, | 382 NotificationConversionHelper::NotificationBitmapToGfxImage( |
439 bitmap_sizes.button_icon_size, | 383 image_scale, |
440 (*options->buttons)[i]->icon_bitmap.get(), | 384 bitmap_sizes.button_icon_size, |
441 &button.icon); | 385 (*options->buttons)[i]->icon_bitmap.get(), |
| 386 &button.icon); |
442 buttons.push_back(button); | 387 buttons.push_back(button); |
443 } | 388 } |
444 notification->set_buttons(buttons); | 389 notification->set_buttons(buttons); |
445 } | 390 } |
446 | 391 |
447 if (options->context_message) { | 392 if (options->context_message) { |
448 notification->set_context_message( | 393 notification->set_context_message( |
449 base::UTF8ToUTF16(*options->context_message)); | 394 base::UTF8ToUTF16(*options->context_message)); |
450 } | 395 } |
451 | 396 |
452 gfx::Image image; | 397 gfx::Image image; |
453 bool has_image = NotificationBitmapToGfxImage(image_scale, | 398 bool has_image = NotificationConversionHelper::NotificationBitmapToGfxImage( |
454 bitmap_sizes.image_size, | 399 image_scale, |
455 options->image_bitmap.get(), | 400 bitmap_sizes.image_size, |
456 &image); | 401 options->image_bitmap.get(), |
| 402 &image); |
457 if (has_image) { | 403 if (has_image) { |
458 // We should have an image if and only if the type is an image type. | 404 // We should have an image if and only if the type is an image type. |
459 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) { | 405 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) { |
460 SetError(kExtraImageProvided); | 406 SetError(kExtraImageProvided); |
461 return false; | 407 return false; |
462 } | 408 } |
463 notification->set_image(image); | 409 notification->set_image(image); |
464 } | 410 } |
465 | 411 |
466 if (options->progress) { | 412 if (options->progress) { |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
684 ? api::notifications::PERMISSION_LEVEL_GRANTED | 630 ? api::notifications::PERMISSION_LEVEL_GRANTED |
685 : api::notifications::PERMISSION_LEVEL_DENIED; | 631 : api::notifications::PERMISSION_LEVEL_DENIED; |
686 | 632 |
687 SetResult(new base::StringValue(api::notifications::ToString(result))); | 633 SetResult(new base::StringValue(api::notifications::ToString(result))); |
688 SendResponse(true); | 634 SendResponse(true); |
689 | 635 |
690 return true; | 636 return true; |
691 } | 637 } |
692 | 638 |
693 } // namespace extensions | 639 } // namespace extensions |
OLD | NEW |