Chromium Code Reviews| 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/notifications/message_center_notification_manager.h" | 5 #include "chrome/browser/notifications/message_center_notification_manager.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
| 9 #include "base/prefs/pref_registry_simple.h" | 9 #include "base/prefs/pref_registry_simple.h" |
| 10 #include "base/prefs/pref_service.h" | 10 #include "base/prefs/pref_service.h" |
| 11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
| 12 #include "base/strings/utf_string_conversions.h" | |
| 12 #include "chrome/browser/chrome_notification_types.h" | 13 #include "chrome/browser/chrome_notification_types.h" |
| 14 #include "chrome/browser/extensions/api/notification_provider/notification_provi der_api.h" | |
| 13 #include "chrome/browser/notifications/desktop_notification_service.h" | 15 #include "chrome/browser/notifications/desktop_notification_service.h" |
| 14 #include "chrome/browser/notifications/desktop_notification_service_factory.h" | 16 #include "chrome/browser/notifications/desktop_notification_service_factory.h" |
| 15 #include "chrome/browser/notifications/fullscreen_notification_blocker.h" | 17 #include "chrome/browser/notifications/fullscreen_notification_blocker.h" |
| 16 #include "chrome/browser/notifications/message_center_settings_controller.h" | 18 #include "chrome/browser/notifications/message_center_settings_controller.h" |
| 17 #include "chrome/browser/notifications/notification.h" | 19 #include "chrome/browser/notifications/notification.h" |
| 18 #include "chrome/browser/notifications/screen_lock_notification_blocker.h" | 20 #include "chrome/browser/notifications/screen_lock_notification_blocker.h" |
| 19 #include "chrome/browser/profiles/profile.h" | 21 #include "chrome/browser/profiles/profile.h" |
| 20 #include "chrome/browser/ui/browser_finder.h" | 22 #include "chrome/browser/ui/browser_finder.h" |
| 21 #include "chrome/browser/ui/chrome_pages.h" | 23 #include "chrome/browser/ui/chrome_pages.h" |
| 22 #include "chrome/browser/ui/host_desktop.h" | 24 #include "chrome/browser/ui/host_desktop.h" |
| 25 #include "chrome/common/extensions/api/notification_provider.h" | |
| 23 #include "chrome/common/pref_names.h" | 26 #include "chrome/common/pref_names.h" |
| 24 #include "content/public/browser/notification_service.h" | 27 #include "content/public/browser/notification_service.h" |
| 25 #include "content/public/browser/web_contents.h" | 28 #include "content/public/browser/web_contents.h" |
| 26 #include "content/public/common/url_constants.h" | 29 #include "content/public/common/url_constants.h" |
| 30 #include "extensions/browser/extension_registry.h" | |
| 27 #include "extensions/browser/extension_system.h" | 31 #include "extensions/browser/extension_system.h" |
| 28 #include "extensions/browser/info_map.h" | 32 #include "extensions/browser/info_map.h" |
| 29 #include "extensions/common/extension_set.h" | 33 #include "extensions/common/extension_set.h" |
| 34 #include "extensions/common/permissions/permissions_data.h" | |
| 30 #include "ui/gfx/image/image_skia.h" | 35 #include "ui/gfx/image/image_skia.h" |
| 31 #include "ui/message_center/message_center_style.h" | 36 #include "ui/message_center/message_center_style.h" |
| 32 #include "ui/message_center/message_center_tray.h" | 37 #include "ui/message_center/message_center_tray.h" |
| 33 #include "ui/message_center/message_center_types.h" | 38 #include "ui/message_center/message_center_types.h" |
| 34 #include "ui/message_center/notifier_settings.h" | 39 #include "ui/message_center/notifier_settings.h" |
| 35 | 40 |
| 36 #if defined(OS_CHROMEOS) | 41 #if defined(OS_CHROMEOS) |
| 37 #include "chrome/browser/notifications/login_state_notification_blocker_chromeos .h" | 42 #include "chrome/browser/notifications/login_state_notification_blocker_chromeos .h" |
| 38 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" | 43 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" |
| 39 #endif | 44 #endif |
| 40 | 45 |
| 41 #if defined(USE_ASH) | 46 #if defined(USE_ASH) |
| 42 #include "ash/shell.h" | 47 #include "ash/shell.h" |
| 43 #include "ash/system/web_notification/web_notification_tray.h" | 48 #include "ash/system/web_notification/web_notification_tray.h" |
| 44 #endif | 49 #endif |
| 45 | 50 |
| 46 #if defined(OS_WIN) | 51 #if defined(OS_WIN) |
| 47 // The first-run balloon will be shown |kFirstRunIdleDelaySeconds| after all | 52 // The first-run balloon will be shown |kFirstRunIdleDelaySeconds| after all |
| 48 // popups go away and the user has notifications in the message center. | 53 // popups go away and the user has notifications in the message center. |
| 49 const int kFirstRunIdleDelaySeconds = 1; | 54 const int kFirstRunIdleDelaySeconds = 1; |
| 50 #endif | 55 #endif |
| 51 | 56 |
| 57 namespace { | |
| 58 | |
| 59 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.
| |
| 60 const gfx::Image* gfx_image, | |
| 61 base::DictionaryValue* return_image_args) { | |
| 62 SkBitmap sk_bitmap = gfx_image->AsBitmap(); | |
| 63 sk_bitmap.lockPixels(); | |
| 64 | |
| 65 return_image_args->SetWithoutPathExpansion( | |
| 66 "width", new base::FundamentalValue(sk_bitmap.width())); | |
| 67 return_image_args->SetWithoutPathExpansion( | |
| 68 "height", new base::FundamentalValue(sk_bitmap.height())); | |
| 69 | |
| 70 const int BYTES_PER_PIXEL = 4; | |
| 71 size_t data_area = sk_bitmap.width() * sk_bitmap.height(); | |
| 72 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.
| |
| 73 | |
| 74 scoped_ptr<char[]> data(new char[data_size]); | |
| 75 uint32_t* bitmap_pixels = sk_bitmap.getAddr32(0, 0); | |
| 76 | |
| 77 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.
| |
| 78 // Bitmap_pixels (original image) is ARGB, data (converted) is RGBA | |
| 79 size_t rgba_index = t * BYTES_PER_PIXEL; | |
| 80 | |
| 81 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.
| |
| 82 data[rgba_index + 1] = (((bitmap_pixels[t]) >> 8) & 0xFF); | |
| 83 data[rgba_index + 2] = (((bitmap_pixels[t]) >> 0) & 0xFF); | |
| 84 data[rgba_index + 3] = (((bitmap_pixels[t]) >> 24) & 0xFF); | |
| 85 } | |
| 86 | |
| 87 scoped_ptr<base::BinaryValue> image_data( | |
| 88 new base::BinaryValue(data.Pass(), data_size)); | |
| 89 return_image_args->SetWithoutPathExpansion("data", image_data.release()); | |
| 90 | |
| 91 return true; | |
| 92 } | |
| 93 | |
| 94 std::string MapTypeToString(message_center::NotificationType type) { | |
| 95 switch (type) { | |
| 96 case message_center::NOTIFICATION_TYPE_BASE_FORMAT: | |
| 97 return "basic"; | |
| 98 case message_center::NOTIFICATION_TYPE_IMAGE: | |
| 99 return "image"; | |
| 100 case message_center::NOTIFICATION_TYPE_MULTIPLE: | |
| 101 return "list"; | |
| 102 case message_center::NOTIFICATION_TYPE_PROGRESS: | |
| 103 return "progress"; | |
| 104 default: | |
| 105 return ""; | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 // This function converts Notification::Notification data to | |
| 110 // base::DictionaryValue | |
| 111 // for extensions::api::notifications::NotificationOptions object construction | |
| 112 void NotificationToNotificationOptionsArgs( | |
| 113 const Notification& notification, | |
| 114 base::DictionaryValue* content_args) { | |
| 115 // 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.
| |
| 116 std::string type = MapTypeToString(notification.type()); | |
| 117 content_args->SetWithoutPathExpansion("type", new base::StringValue(type)); | |
| 118 | |
| 119 content_args->SetWithoutPathExpansion( | |
| 120 "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
| |
| 121 | |
| 122 content_args->SetWithoutPathExpansion( | |
| 123 "message", new base::StringValue(notification.message())); | |
| 124 | |
| 125 if (!notification.icon().IsEmpty()) { | |
| 126 scoped_ptr<base::DictionaryValue> icon_args(new base::DictionaryValue); | |
| 127 if (GfxImageToNotificationBitmapArgs(¬ification.icon(), icon_args.get())) | |
| 128 content_args->SetWithoutPathExpansion("iconBitmap", icon_args.release()); | |
| 129 } | |
| 130 | |
| 131 // Handle optional data provided. | |
| 132 const message_center::RichNotificationData* rich_data = | |
| 133 ¬ification.rich_notification_data(); | |
| 134 | |
| 135 content_args->SetWithoutPathExpansion( | |
| 136 "priority", new base::FundamentalValue(rich_data->priority)); | |
| 137 | |
| 138 content_args->SetWithoutPathExpansion( | |
| 139 "isClickable", new base::FundamentalValue(rich_data->clickable)); | |
| 140 | |
| 141 content_args->SetWithoutPathExpansion( | |
| 142 "eventTime", | |
| 143 new base::FundamentalValue(rich_data->timestamp.ToDoubleT())); | |
| 144 | |
| 145 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.
| |
| 146 content_args->SetWithoutPathExpansion( | |
| 147 "contextMessage", new base::StringValue(rich_data->context_message)); | |
| 148 | |
| 149 if (!rich_data->buttons.empty()) { | |
| 150 scoped_ptr<base::ListValue> button_list(new base::ListValue); | |
| 151 for (size_t i = 0; i < rich_data->buttons.size(); i++) { | |
| 152 scoped_ptr<base::DictionaryValue> button(new base::DictionaryValue()); | |
| 153 button->SetWithoutPathExpansion( | |
| 154 "title", new base::StringValue(rich_data->buttons[i].title)); | |
| 155 | |
| 156 if (!rich_data->buttons[i].icon.IsEmpty()) { | |
| 157 scoped_ptr<base::DictionaryValue> icon_args(new base::DictionaryValue); | |
| 158 if (GfxImageToNotificationBitmapArgs(&rich_data->buttons[i].icon, | |
| 159 icon_args.get())) | |
| 160 button->SetWithoutPathExpansion("iconBitmap", icon_args.release()); | |
| 161 } | |
| 162 button_list->Set(i, button.release()); | |
| 163 } | |
| 164 content_args->SetWithoutPathExpansion("buttons", button_list.release()); | |
| 165 } | |
| 166 | |
| 167 // Only image type notifications should have images. | |
| 168 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.
| |
| 169 scoped_ptr<base::DictionaryValue> image_args(new base::DictionaryValue); | |
| 170 if (GfxImageToNotificationBitmapArgs(¬ification.image(), | |
| 171 image_args.get())) | |
| 172 content_args->SetWithoutPathExpansion("imageBitmap", | |
| 173 image_args.release()); | |
| 174 } | |
| 175 | |
| 176 // Only progress type notifications should have progress bars. | |
| 177 if (type == "progress") | |
| 178 content_args->SetWithoutPathExpansion( | |
| 179 "progress", new base::FundamentalValue(rich_data->progress)); | |
| 180 | |
| 181 // Only list type notifications should have lists. | |
| 182 if (type == "list" && !rich_data->items.empty()) { | |
| 183 scoped_ptr<base::ListValue> list(new base::ListValue); | |
| 184 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.
| |
| 185 scoped_ptr<base::DictionaryValue> item(new base::DictionaryValue()); | |
| 186 item->SetWithoutPathExpansion( | |
| 187 "title", new base::StringValue(rich_data->items[i].title)); | |
| 188 item->SetWithoutPathExpansion( | |
| 189 "message", new base::StringValue(rich_data->items[i].message)); | |
| 190 list->Set(i, item.release()); | |
| 191 } | |
| 192 content_args->SetWithoutPathExpansion("items", list.release()); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 // TODO(liyanhou): When additional settings in Chrome Settings is implemented, | |
| 197 // 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.
| |
| 198 std::string GetExtensionTakingOverNotifications(Profile* profile) { | |
| 199 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
| |
| 200 extensions::ExtensionRegistry::Get(profile); | |
| 201 std::string app_id; | |
| 202 for (extensions::ExtensionSet::const_iterator it = | |
| 203 registry->enabled_extensions().begin(); | |
| 204 it != registry->enabled_extensions().end(); | |
| 205 ++it) { | |
| 206 if ((*it->get()).permissions_data()->HasAPIPermission( | |
| 207 extensions::APIPermission::ID::kNotificationProvider)) | |
| 208 app_id = (*it->get()).id(); | |
| 209 } | |
| 210 return app_id; | |
| 211 } | |
| 212 } // namespace | |
| 213 | |
| 52 MessageCenterNotificationManager::MessageCenterNotificationManager( | 214 MessageCenterNotificationManager::MessageCenterNotificationManager( |
| 53 message_center::MessageCenter* message_center, | 215 message_center::MessageCenter* message_center, |
| 54 PrefService* local_state, | 216 PrefService* local_state, |
| 55 scoped_ptr<message_center::NotifierSettingsProvider> settings_provider) | 217 scoped_ptr<message_center::NotifierSettingsProvider> settings_provider) |
| 56 : message_center_(message_center), | 218 : message_center_(message_center), |
| 57 #if defined(OS_WIN) | 219 #if defined(OS_WIN) |
| 58 first_run_idle_timeout_( | 220 first_run_idle_timeout_( |
| 59 base::TimeDelta::FromSeconds(kFirstRunIdleDelaySeconds)), | 221 base::TimeDelta::FromSeconds(kFirstRunIdleDelaySeconds)), |
| 60 weak_factory_(this), | 222 weak_factory_(this), |
| 61 #endif | 223 #endif |
| (...skipping 424 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 486 std::string id = notification.delegate_id(); | 648 std::string id = notification.delegate_id(); |
| 487 // Notification ids should be unique. | 649 // Notification ids should be unique. |
| 488 DCHECK(profile_notifications_.find(id) == profile_notifications_.end()); | 650 DCHECK(profile_notifications_.find(id) == profile_notifications_.end()); |
| 489 profile_notifications_[id] = profile_notification; | 651 profile_notifications_[id] = profile_notification; |
| 490 | 652 |
| 491 // Create the copy for message center, and ensure the extension ID is correct. | 653 // Create the copy for message center, and ensure the extension ID is correct. |
| 492 scoped_ptr<message_center::Notification> message_center_notification( | 654 scoped_ptr<message_center::Notification> message_center_notification( |
| 493 new message_center::Notification(notification)); | 655 new message_center::Notification(notification)); |
| 494 message_center_->AddNotification(message_center_notification.Pass()); | 656 message_center_->AddNotification(message_center_notification.Pass()); |
| 495 | 657 |
| 658 // TODO(liyanhou): Change the logic to only send notifications to one party. | |
| 659 // Currently, if there's an app with notificationProvider permission, | |
| 660 // 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.
| |
| 661 | |
|
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.
| |
| 662 // If there exists apps/extensions that have notificationProvider permission, | |
| 663 // reroute notifications to one of the apps/extensions. | |
| 664 std::string app_id = | |
| 665 GetExtensionTakingOverNotifications(profile_notification->profile()); | |
| 666 if (!app_id.empty()) { | |
| 667 const std::string sender_id = notification.notifier_id().id; | |
| 668 | |
| 669 // Extract data from Notification. | |
| 670 scoped_ptr<base::DictionaryValue> content_args(new base::DictionaryValue); | |
| 671 NotificationToNotificationOptionsArgs(notification, content_args.get()); | |
| 672 | |
| 673 scoped_ptr<extensions::api::notifications::NotificationOptions> content( | |
| 674 new extensions::api::notifications::NotificationOptions()); | |
| 675 extensions::api::notifications::NotificationOptions::Populate( | |
| 676 *(content_args.get()), content.get()); | |
| 677 | |
| 678 // Fire event to send the data to an extension/app. | |
| 679 scoped_ptr<extensions::NotificationProviderEventRouter> event_router( | |
| 680 new extensions::NotificationProviderEventRouter( | |
| 681 profile_notification->profile())); | |
| 682 event_router->CreateNotification( | |
| 683 app_id, sender_id, id, *(content.release())); | |
| 684 } | |
| 685 | |
| 496 profile_notification->StartDownloads(); | 686 profile_notification->StartDownloads(); |
| 497 } | 687 } |
| 498 | 688 |
| 499 void MessageCenterNotificationManager::RemoveProfileNotification( | 689 void MessageCenterNotificationManager::RemoveProfileNotification( |
| 500 ProfileNotification* profile_notification) { | 690 ProfileNotification* profile_notification) { |
| 501 std::string id = profile_notification->notification().delegate_id(); | 691 std::string id = profile_notification->notification().delegate_id(); |
| 502 profile_notifications_.erase(id); | 692 profile_notifications_.erase(id); |
| 503 delete profile_notification; | 693 delete profile_notification; |
| 504 } | 694 } |
| 505 | 695 |
| 506 MessageCenterNotificationManager::ProfileNotification* | 696 MessageCenterNotificationManager::ProfileNotification* |
| 507 MessageCenterNotificationManager::FindProfileNotification( | 697 MessageCenterNotificationManager::FindProfileNotification( |
| 508 const std::string& id) const { | 698 const std::string& id) const { |
| 509 NotificationMap::const_iterator iter = profile_notifications_.find(id); | 699 NotificationMap::const_iterator iter = profile_notifications_.find(id); |
| 510 if (iter == profile_notifications_.end()) | 700 if (iter == profile_notifications_.end()) |
| 511 return NULL; | 701 return NULL; |
| 512 | 702 |
| 513 return (*iter).second; | 703 return (*iter).second; |
| 514 } | 704 } |
| OLD | NEW |