Chromium Code Reviews| Index: chrome/browser/notifications/message_center_notification_manager.cc |
| diff --git a/chrome/browser/notifications/message_center_notification_manager.cc b/chrome/browser/notifications/message_center_notification_manager.cc |
| index 8afa5a1714d82d9df32ebe9b709082e145d3dd02..11e7ee1a2d0eff9e80eee3990b7e421afe425d94 100644 |
| --- a/chrome/browser/notifications/message_center_notification_manager.cc |
| +++ b/chrome/browser/notifications/message_center_notification_manager.cc |
| @@ -9,7 +9,9 @@ |
| #include "base/prefs/pref_registry_simple.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/stl_util.h" |
| +#include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| +#include "chrome/browser/extensions/api/notification_provider/notification_provider_api.h" |
| #include "chrome/browser/notifications/desktop_notification_service.h" |
| #include "chrome/browser/notifications/desktop_notification_service_factory.h" |
| #include "chrome/browser/notifications/fullscreen_notification_blocker.h" |
| @@ -20,13 +22,16 @@ |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/chrome_pages.h" |
| #include "chrome/browser/ui/host_desktop.h" |
| +#include "chrome/common/extensions/api/notification_provider.h" |
| #include "chrome/common/pref_names.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/url_constants.h" |
| +#include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/info_map.h" |
| #include "extensions/common/extension_set.h" |
| +#include "extensions/common/permissions/permissions_data.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/message_center/message_center_style.h" |
| #include "ui/message_center/message_center_tray.h" |
| @@ -49,6 +54,163 @@ |
| const int kFirstRunIdleDelaySeconds = 1; |
| #endif |
| +namespace { |
| + |
| +bool GfxImageToNotificationBitmapArgs( |
|
Pete Williamson
2014/08/05 22:03:52
Why return bool when this has no failure case? Ma
liyanhou
2014/08/08 22:40:25
Done.
|
| + const gfx::Image* gfx_image, |
| + base::DictionaryValue* return_image_args) { |
| + SkBitmap sk_bitmap = gfx_image->AsBitmap(); |
| + sk_bitmap.lockPixels(); |
| + |
| + return_image_args->SetWithoutPathExpansion( |
| + "width", new base::FundamentalValue(sk_bitmap.width())); |
| + return_image_args->SetWithoutPathExpansion( |
| + "height", new base::FundamentalValue(sk_bitmap.height())); |
| + |
| + const int BYTES_PER_PIXEL = 4; |
| + size_t data_area = sk_bitmap.width() * sk_bitmap.height(); |
| + size_t data_size = data_area * BYTES_PER_PIXEL; |
|
Pete Williamson
2014/08/05 22:03:52
Do we know for sure that all input bitmaps will be
liyanhou
2014/08/08 22:40:25
Yes. All input images are of gfx::Image type.
|
| + |
| + scoped_ptr<char[]> data(new char[data_size]); |
| + uint32_t* bitmap_pixels = sk_bitmap.getAddr32(0, 0); |
| + |
| + for (size_t t = 0; t < data_area; ++t) { |
|
Pete Williamson
2014/08/05 22:03:52
nit - "i" is normally used as an iterator variable
liyanhou
2014/08/08 22:40:25
Done.
|
| + // Bitmap_pixels (original image) is ARGB, data (converted) is RGBA |
| + size_t rgba_index = t * BYTES_PER_PIXEL; |
| + |
| + data[rgba_index + 0] = (((bitmap_pixels[t]) >> 16) & 0xFF); |
|
Pete Williamson
2014/08/05 22:03:52
Have you checked elsewhere in the code to see if t
liyanhou
2014/08/08 22:40:24
Done.
|
| + data[rgba_index + 1] = (((bitmap_pixels[t]) >> 8) & 0xFF); |
| + data[rgba_index + 2] = (((bitmap_pixels[t]) >> 0) & 0xFF); |
| + data[rgba_index + 3] = (((bitmap_pixels[t]) >> 24) & 0xFF); |
| + } |
| + |
| + scoped_ptr<base::BinaryValue> image_data( |
| + new base::BinaryValue(data.Pass(), data_size)); |
| + return_image_args->SetWithoutPathExpansion("data", image_data.release()); |
| + |
| + return true; |
| +} |
| + |
| +std::string MapTypeToString(message_center::NotificationType type) { |
| + switch (type) { |
| + case message_center::NOTIFICATION_TYPE_BASE_FORMAT: |
| + return "basic"; |
| + case message_center::NOTIFICATION_TYPE_IMAGE: |
| + return "image"; |
| + case message_center::NOTIFICATION_TYPE_MULTIPLE: |
| + return "list"; |
| + case message_center::NOTIFICATION_TYPE_PROGRESS: |
| + return "progress"; |
| + default: |
| + return ""; |
| + } |
| +} |
| + |
| +// This function converts Notification::Notification data to |
| +// base::DictionaryValue |
| +// for extensions::api::notifications::NotificationOptions object construction |
| +void NotificationToNotificationOptionsArgs( |
| + const Notification& notification, |
| + base::DictionaryValue* content_args) { |
| + // Extract required fileds: type, title, message, and icon. |
|
Pete Williamson
2014/08/05 22:03:52
nit: fileds: fields
liyanhou
2014/08/08 22:40:25
Done.
|
| + std::string type = MapTypeToString(notification.type()); |
| + content_args->SetWithoutPathExpansion("type", new base::StringValue(type)); |
| + |
| + content_args->SetWithoutPathExpansion( |
| + "title", new base::StringValue(notification.title())); |
|
Pete Williamson
2014/08/05 22:03:52
can notification.title() (or any of the other meth
liyanhou
2014/08/08 22:40:24
id, title, message, and icon url are the required
Pete Williamson
2014/08/09 00:38:19
Thanks! Just to be sure, can you check that the n
liyanhou
2014/08/11 17:59:23
Yes. Notifications API enforces it when creating a
|
| + |
| + content_args->SetWithoutPathExpansion( |
| + "message", new base::StringValue(notification.message())); |
| + |
| + if (!notification.icon().IsEmpty()) { |
| + scoped_ptr<base::DictionaryValue> icon_args(new base::DictionaryValue); |
| + if (GfxImageToNotificationBitmapArgs(¬ification.icon(), icon_args.get())) |
| + content_args->SetWithoutPathExpansion("iconBitmap", icon_args.release()); |
| + } |
| + |
| + // Handle optional data provided. |
| + const message_center::RichNotificationData* rich_data = |
| + ¬ification.rich_notification_data(); |
| + |
| + content_args->SetWithoutPathExpansion( |
| + "priority", new base::FundamentalValue(rich_data->priority)); |
| + |
| + content_args->SetWithoutPathExpansion( |
| + "isClickable", new base::FundamentalValue(rich_data->clickable)); |
| + |
| + content_args->SetWithoutPathExpansion( |
| + "eventTime", |
| + new base::FundamentalValue(rich_data->timestamp.ToDoubleT())); |
| + |
| + if (!base::UTF16ToUTF8(rich_data->context_message).empty()) |
|
Pete Williamson
2014/08/05 22:03:52
Can we check emptyness without doing a conversion?
liyanhou
2014/08/08 22:40:25
Done.
|
| + content_args->SetWithoutPathExpansion( |
| + "contextMessage", new base::StringValue(rich_data->context_message)); |
| + |
| + if (!rich_data->buttons.empty()) { |
| + scoped_ptr<base::ListValue> button_list(new base::ListValue); |
| + for (size_t i = 0; i < rich_data->buttons.size(); i++) { |
| + scoped_ptr<base::DictionaryValue> button(new base::DictionaryValue()); |
| + button->SetWithoutPathExpansion( |
| + "title", new base::StringValue(rich_data->buttons[i].title)); |
| + |
| + if (!rich_data->buttons[i].icon.IsEmpty()) { |
| + scoped_ptr<base::DictionaryValue> icon_args(new base::DictionaryValue); |
| + if (GfxImageToNotificationBitmapArgs(&rich_data->buttons[i].icon, |
| + icon_args.get())) |
| + button->SetWithoutPathExpansion("iconBitmap", icon_args.release()); |
| + } |
| + button_list->Set(i, button.release()); |
| + } |
| + content_args->SetWithoutPathExpansion("buttons", button_list.release()); |
| + } |
| + |
| + // Only image type notifications should have images. |
| + if (type == "image" && !rich_data->image.IsEmpty()) { |
|
Pete Williamson
2014/08/05 22:03:52
maybe log if we find one that violates this assump
liyanhou
2014/08/08 22:40:25
Done.
|
| + scoped_ptr<base::DictionaryValue> image_args(new base::DictionaryValue); |
| + if (GfxImageToNotificationBitmapArgs(¬ification.image(), |
| + image_args.get())) |
| + content_args->SetWithoutPathExpansion("imageBitmap", |
| + image_args.release()); |
| + } |
| + |
| + // Only progress type notifications should have progress bars. |
| + if (type == "progress") |
| + content_args->SetWithoutPathExpansion( |
| + "progress", new base::FundamentalValue(rich_data->progress)); |
| + |
| + // Only list type notifications should have lists. |
| + if (type == "list" && !rich_data->items.empty()) { |
| + scoped_ptr<base::ListValue> list(new base::ListValue); |
| + for (size_t i = 0; i < rich_data->items.size(); i++) { |
|
Pete Williamson
2014/08/05 22:03:52
use "j" instead of "i" here to keep it distinct fr
liyanhou
2014/08/08 22:40:24
Done.
|
| + scoped_ptr<base::DictionaryValue> item(new base::DictionaryValue()); |
| + item->SetWithoutPathExpansion( |
| + "title", new base::StringValue(rich_data->items[i].title)); |
| + item->SetWithoutPathExpansion( |
| + "message", new base::StringValue(rich_data->items[i].message)); |
| + list->Set(i, item.release()); |
| + } |
| + content_args->SetWithoutPathExpansion("items", list.release()); |
| + } |
| +} |
| + |
| +// TODO(liyanhou): When additional settings in Chrome Settings is implemented, |
| +// change choosing a random app with this permission to a user selected app. |
|
Pete Williamson
2014/08/05 22:03:53
nit: We're not actually getting a random app here
liyanhou
2014/08/08 22:40:25
Done.
|
| +std::string GetExtensionTakingOverNotifications(Profile* profile) { |
| + extensions::ExtensionRegistry* registry = |
|
Pete Williamson
2014/08/05 22:03:52
Can this return null (maybe early in setup before
liyanhou
2014/08/08 22:40:25
I don't really know. I read cases where they check
|
| + extensions::ExtensionRegistry::Get(profile); |
| + std::string app_id; |
| + for (extensions::ExtensionSet::const_iterator it = |
| + registry->enabled_extensions().begin(); |
| + it != registry->enabled_extensions().end(); |
| + ++it) { |
| + if ((*it->get()).permissions_data()->HasAPIPermission( |
| + extensions::APIPermission::ID::kNotificationProvider)) |
| + app_id = (*it->get()).id(); |
| + } |
| + return app_id; |
| +} |
| +} // namespace |
| + |
| MessageCenterNotificationManager::MessageCenterNotificationManager( |
| message_center::MessageCenter* message_center, |
| PrefService* local_state, |
| @@ -493,6 +655,34 @@ void MessageCenterNotificationManager::AddProfileNotification( |
| new message_center::Notification(notification)); |
| message_center_->AddNotification(message_center_notification.Pass()); |
| + // TODO(liyanhou): Change the logic to only send notifications to one party. |
| + // Currently, if there's an app with notificationProvider permission, |
| + // notifications will go to both Chrome Notification Center and that app. |
|
dewittj
2014/08/07 22:49:08
I don't believe this code should live in AddProfil
liyanhou
2014/08/08 22:40:25
Done. No, this patch doesn't forward updates.
|
| + |
|
Pete Williamson
2014/08/05 22:03:52
We should not check this in turned on. It is OK t
liyanhou
2014/08/08 22:40:25
Yes it is in trunk.
|
| + // If there exists apps/extensions that have notificationProvider permission, |
| + // reroute notifications to one of the apps/extensions. |
| + std::string app_id = |
| + GetExtensionTakingOverNotifications(profile_notification->profile()); |
| + if (!app_id.empty()) { |
| + const std::string sender_id = notification.notifier_id().id; |
| + |
| + // Extract data from Notification. |
| + scoped_ptr<base::DictionaryValue> content_args(new base::DictionaryValue); |
| + NotificationToNotificationOptionsArgs(notification, content_args.get()); |
| + |
| + scoped_ptr<extensions::api::notifications::NotificationOptions> content( |
| + new extensions::api::notifications::NotificationOptions()); |
| + extensions::api::notifications::NotificationOptions::Populate( |
| + *(content_args.get()), content.get()); |
| + |
| + // Fire event to send the data to an extension/app. |
| + scoped_ptr<extensions::NotificationProviderEventRouter> event_router( |
| + new extensions::NotificationProviderEventRouter( |
| + profile_notification->profile())); |
| + event_router->CreateNotification( |
| + app_id, sender_id, id, *(content.release())); |
| + } |
| + |
| profile_notification->StartDownloads(); |
| } |