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