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" |
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 Loading... |
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 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
247 // First, make sure the required fields exist: type, title, message, icon. | 259 // First, make sure the required fields exist: type, title, message, icon. |
248 // These fields are defined as optional in IDL such that they can be used as | 260 // These fields are defined as optional in IDL such that they can be used as |
249 // optional for notification updates. But for notification creations, they | 261 // optional for notification updates. But for notification creations, they |
250 // should be present. | 262 // should be present. |
251 if (options->type == api::notifications::TEMPLATE_TYPE_NONE || | 263 if (options->type == api::notifications::TEMPLATE_TYPE_NONE || |
252 !options->icon_url || !options->title || !options->message) { | 264 !options->icon_url || !options->title || !options->message) { |
253 SetError(kMissingRequiredPropertiesForCreateNotification); | 265 SetError(kMissingRequiredPropertiesForCreateNotification); |
254 return false; | 266 return false; |
255 } | 267 } |
256 | 268 |
| 269 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes(); |
| 270 |
| 271 float image_scale = |
| 272 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back()); |
| 273 |
257 // Extract required fields: type, title, message, and icon. | 274 // Extract required fields: type, title, message, and icon. |
258 message_center::NotificationType type = | 275 message_center::NotificationType type = |
259 MapApiTemplateTypeToType(options->type); | 276 MapApiTemplateTypeToType(options->type); |
260 const base::string16 title(base::UTF8ToUTF16(*options->title)); | 277 const base::string16 title(base::UTF8ToUTF16(*options->title)); |
261 const base::string16 message(base::UTF8ToUTF16(*options->message)); | 278 const base::string16 message(base::UTF8ToUTF16(*options->message)); |
262 gfx::Image icon; | 279 gfx::Image icon; |
263 | 280 |
264 // TODO(dewittj): Return error if this fails. | 281 if (!NotificationBitmapToGfxImage(image_scale, |
265 NotificationBitmapToGfxImage(options->icon_bitmap.get(), &icon); | 282 bitmap_sizes.icon_size, |
| 283 options->icon_bitmap.get(), |
| 284 &icon)) { |
| 285 SetError(kUnableToDecodeIconError); |
| 286 return false; |
| 287 } |
266 | 288 |
267 // Then, handle any optional data that's been provided. | 289 // Then, handle any optional data that's been provided. |
268 message_center::RichNotificationData optional_fields; | 290 message_center::RichNotificationData optional_fields; |
269 if (options->priority.get()) | 291 if (options->priority.get()) |
270 optional_fields.priority = *options->priority; | 292 optional_fields.priority = *options->priority; |
271 | 293 |
272 if (options->event_time.get()) | 294 if (options->event_time.get()) |
273 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time); | 295 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time); |
274 | 296 |
275 if (options->buttons.get()) { | 297 if (options->buttons.get()) { |
276 // Currently we allow up to 2 buttons. | 298 // Currently we allow up to 2 buttons. |
277 size_t number_of_buttons = options->buttons->size(); | 299 size_t number_of_buttons = options->buttons->size(); |
278 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; | 300 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; |
279 | 301 |
280 for (size_t i = 0; i < number_of_buttons; i++) { | 302 for (size_t i = 0; i < number_of_buttons; i++) { |
281 message_center::ButtonInfo info( | 303 message_center::ButtonInfo info( |
282 base::UTF8ToUTF16((*options->buttons)[i]->title)); | 304 base::UTF8ToUTF16((*options->buttons)[i]->title)); |
283 NotificationBitmapToGfxImage((*options->buttons)[i]->icon_bitmap.get(), | 305 NotificationBitmapToGfxImage(image_scale, |
284 &info.icon); | 306 bitmap_sizes.button_icon_size, |
| 307 (*options->buttons)[i]->icon_bitmap.get(), |
| 308 &info.icon); |
285 optional_fields.buttons.push_back(info); | 309 optional_fields.buttons.push_back(info); |
286 } | 310 } |
287 } | 311 } |
288 | 312 |
289 if (options->context_message) { | 313 if (options->context_message) { |
290 optional_fields.context_message = | 314 optional_fields.context_message = |
291 base::UTF8ToUTF16(*options->context_message); | 315 base::UTF8ToUTF16(*options->context_message); |
292 } | 316 } |
293 | 317 |
294 bool has_image = NotificationBitmapToGfxImage(options->image_bitmap.get(), | 318 bool has_image = NotificationBitmapToGfxImage(image_scale, |
| 319 bitmap_sizes.image_size, |
| 320 options->image_bitmap.get(), |
295 &optional_fields.image); | 321 &optional_fields.image); |
296 // We should have an image if and only if the type is an image type. | 322 // We should have an image if and only if the type is an image type. |
297 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) | 323 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) { |
| 324 SetError(kExtraImageProvided); |
298 return false; | 325 return false; |
| 326 } |
299 | 327 |
300 // We should have list items if and only if the type is a multiple type. | 328 // We should have list items if and only if the type is a multiple type. |
301 bool has_list_items = options->items.get() && options->items->size() > 0; | 329 bool has_list_items = options->items.get() && options->items->size() > 0; |
302 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) | 330 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) { |
| 331 SetError(kExtraListItemsProvided); |
303 return false; | 332 return false; |
| 333 } |
304 | 334 |
305 if (options->progress.get() != NULL) { | 335 if (options->progress.get() != NULL) { |
306 // We should have progress if and only if the type is a progress type. | 336 // We should have progress if and only if the type is a progress type. |
307 if (type != message_center::NOTIFICATION_TYPE_PROGRESS) { | 337 if (type != message_center::NOTIFICATION_TYPE_PROGRESS) { |
308 SetError(kUnexpectedProgressValueForNonProgressType); | 338 SetError(kUnexpectedProgressValueForNonProgressType); |
309 return false; | 339 return false; |
310 } | 340 } |
311 optional_fields.progress = *options->progress; | 341 optional_fields.progress = *options->progress; |
312 // Progress value should range from 0 to 100. | 342 // Progress value should range from 0 to 100. |
313 if (optional_fields.progress < 0 || optional_fields.progress > 100) { | 343 if (optional_fields.progress < 0 || optional_fields.progress > 100) { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 api_delegate); | 378 api_delegate); |
349 | 379 |
350 g_browser_process->notification_ui_manager()->Add(notification, GetProfile()); | 380 g_browser_process->notification_ui_manager()->Add(notification, GetProfile()); |
351 return true; | 381 return true; |
352 } | 382 } |
353 | 383 |
354 bool NotificationsApiFunction::UpdateNotification( | 384 bool NotificationsApiFunction::UpdateNotification( |
355 const std::string& id, | 385 const std::string& id, |
356 api::notifications::NotificationOptions* options, | 386 api::notifications::NotificationOptions* options, |
357 Notification* notification) { | 387 Notification* notification) { |
| 388 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes(); |
| 389 float image_scale = |
| 390 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back()); |
| 391 |
358 // Update optional fields if provided. | 392 // Update optional fields if provided. |
359 if (options->type != api::notifications::TEMPLATE_TYPE_NONE) | 393 if (options->type != api::notifications::TEMPLATE_TYPE_NONE) |
360 notification->set_type(MapApiTemplateTypeToType(options->type)); | 394 notification->set_type(MapApiTemplateTypeToType(options->type)); |
361 if (options->title) | 395 if (options->title) |
362 notification->set_title(base::UTF8ToUTF16(*options->title)); | 396 notification->set_title(base::UTF8ToUTF16(*options->title)); |
363 if (options->message) | 397 if (options->message) |
364 notification->set_message(base::UTF8ToUTF16(*options->message)); | 398 notification->set_message(base::UTF8ToUTF16(*options->message)); |
365 | 399 |
366 // TODO(dewittj): Return error if this fails. | 400 // TODO(dewittj): Return error if this fails. |
367 if (options->icon_bitmap) { | 401 if (options->icon_bitmap) { |
368 gfx::Image icon; | 402 gfx::Image icon; |
369 NotificationBitmapToGfxImage(options->icon_bitmap.get(), &icon); | 403 NotificationBitmapToGfxImage( |
| 404 image_scale, bitmap_sizes.icon_size, options->icon_bitmap.get(), &icon); |
370 notification->set_icon(icon); | 405 notification->set_icon(icon); |
371 } | 406 } |
372 | 407 |
373 if (options->priority) | 408 if (options->priority) |
374 notification->set_priority(*options->priority); | 409 notification->set_priority(*options->priority); |
375 | 410 |
376 if (options->event_time) | 411 if (options->event_time) |
377 notification->set_timestamp(base::Time::FromJsTime(*options->event_time)); | 412 notification->set_timestamp(base::Time::FromJsTime(*options->event_time)); |
378 | 413 |
379 if (options->buttons) { | 414 if (options->buttons) { |
380 // Currently we allow up to 2 buttons. | 415 // Currently we allow up to 2 buttons. |
381 size_t number_of_buttons = options->buttons->size(); | 416 size_t number_of_buttons = options->buttons->size(); |
382 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; | 417 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons; |
383 | 418 |
384 std::vector<message_center::ButtonInfo> buttons; | 419 std::vector<message_center::ButtonInfo> buttons; |
385 for (size_t i = 0; i < number_of_buttons; i++) { | 420 for (size_t i = 0; i < number_of_buttons; i++) { |
386 message_center::ButtonInfo button( | 421 message_center::ButtonInfo button( |
387 base::UTF8ToUTF16((*options->buttons)[i]->title)); | 422 base::UTF8ToUTF16((*options->buttons)[i]->title)); |
388 NotificationBitmapToGfxImage((*options->buttons)[i]->icon_bitmap.get(), | 423 NotificationBitmapToGfxImage(image_scale, |
389 &button.icon); | 424 bitmap_sizes.button_icon_size, |
| 425 (*options->buttons)[i]->icon_bitmap.get(), |
| 426 &button.icon); |
390 buttons.push_back(button); | 427 buttons.push_back(button); |
391 } | 428 } |
392 notification->set_buttons(buttons); | 429 notification->set_buttons(buttons); |
393 } | 430 } |
394 | 431 |
395 if (options->context_message) { | 432 if (options->context_message) { |
396 notification->set_context_message( | 433 notification->set_context_message( |
397 base::UTF8ToUTF16(*options->context_message)); | 434 base::UTF8ToUTF16(*options->context_message)); |
398 } | 435 } |
399 | 436 |
400 gfx::Image image; | 437 gfx::Image image; |
401 if (NotificationBitmapToGfxImage(options->image_bitmap.get(), &image)) { | 438 bool has_image = NotificationBitmapToGfxImage(image_scale, |
| 439 bitmap_sizes.image_size, |
| 440 options->image_bitmap.get(), |
| 441 &image); |
| 442 if (has_image) { |
402 // We should have an image if and only if the type is an image type. | 443 // We should have an image if and only if the type is an image type. |
403 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) | 444 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) { |
| 445 SetError(kExtraImageProvided); |
404 return false; | 446 return false; |
| 447 } |
405 notification->set_image(image); | 448 notification->set_image(image); |
406 } | 449 } |
407 | 450 |
408 if (options->progress) { | 451 if (options->progress) { |
409 // We should have progress if and only if the type is a progress type. | 452 // We should have progress if and only if the type is a progress type. |
410 if (notification->type() != message_center::NOTIFICATION_TYPE_PROGRESS) { | 453 if (notification->type() != message_center::NOTIFICATION_TYPE_PROGRESS) { |
411 SetError(kUnexpectedProgressValueForNonProgressType); | 454 SetError(kUnexpectedProgressValueForNonProgressType); |
412 return false; | 455 return false; |
413 } | 456 } |
414 int progress = *options->progress; | 457 int progress = *options->progress; |
415 // Progress value should range from 0 to 100. | 458 // Progress value should range from 0 to 100. |
416 if (progress < 0 || progress > 100) { | 459 if (progress < 0 || progress > 100) { |
417 SetError(kInvalidProgressValue); | 460 SetError(kInvalidProgressValue); |
418 return false; | 461 return false; |
419 } | 462 } |
420 notification->set_progress(progress); | 463 notification->set_progress(progress); |
421 } | 464 } |
422 | 465 |
423 if (options->items.get() && options->items->size() > 0) { | 466 if (options->items.get() && options->items->size() > 0) { |
424 // We should have list items if and only if the type is a multiple type. | 467 // We should have list items if and only if the type is a multiple type. |
425 if (notification->type() != message_center::NOTIFICATION_TYPE_MULTIPLE) | 468 if (notification->type() != message_center::NOTIFICATION_TYPE_MULTIPLE) { |
| 469 SetError(kExtraListItemsProvided); |
426 return false; | 470 return false; |
| 471 } |
427 | 472 |
428 std::vector<message_center::NotificationItem> items; | 473 std::vector<message_center::NotificationItem> items; |
429 using api::notifications::NotificationItem; | 474 using api::notifications::NotificationItem; |
430 std::vector<linked_ptr<NotificationItem> >::iterator i; | 475 std::vector<linked_ptr<NotificationItem> >::iterator i; |
431 for (i = options->items->begin(); i != options->items->end(); ++i) { | 476 for (i = options->items->begin(); i != options->items->end(); ++i) { |
432 message_center::NotificationItem item( | 477 message_center::NotificationItem item( |
433 base::UTF8ToUTF16(i->get()->title), | 478 base::UTF8ToUTF16(i->get()->title), |
434 base::UTF8ToUTF16(i->get()->message)); | 479 base::UTF8ToUTF16(i->get()->message)); |
435 items.push_back(item); | 480 items.push_back(item); |
436 } | 481 } |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
624 ? api::notifications::PERMISSION_LEVEL_GRANTED | 669 ? api::notifications::PERMISSION_LEVEL_GRANTED |
625 : api::notifications::PERMISSION_LEVEL_DENIED; | 670 : api::notifications::PERMISSION_LEVEL_DENIED; |
626 | 671 |
627 SetResult(new base::StringValue(api::notifications::ToString(result))); | 672 SetResult(new base::StringValue(api::notifications::ToString(result))); |
628 SendResponse(true); | 673 SendResponse(true); |
629 | 674 |
630 return true; | 675 return true; |
631 } | 676 } |
632 | 677 |
633 } // namespace extensions | 678 } // namespace extensions |
OLD | NEW |