| 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 <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| 11 #include "base/callback.h" | 11 #include "base/callback.h" |
| 12 #include "base/feature_list.h" | 12 #include "base/feature_list.h" |
| 13 #include "base/guid.h" | 13 #include "base/guid.h" |
| 14 #include "base/macros.h" | 14 #include "base/macros.h" |
| 15 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
| 16 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
| 17 #include "base/rand_util.h" | 17 #include "base/rand_util.h" |
| 18 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
| 20 #include "base/time/time.h" | 20 #include "base/time/time.h" |
| 21 #include "build/build_config.h" | 21 #include "build/build_config.h" |
| 22 #include "chrome/browser/browser_process.h" | 22 #include "chrome/browser/browser_process.h" |
| 23 #include "chrome/browser/extensions/api/notifications/extension_notification_dis
play_helper.h" | 23 #include "chrome/browser/extensions/api/notifications/extension_notification_dis
play_helper.h" |
| 24 #include "chrome/browser/extensions/api/notifications/extension_notification_dis
play_helper_factory.h" | 24 #include "chrome/browser/extensions/api/notifications/extension_notification_dis
play_helper_factory.h" |
| 25 #include "chrome/browser/notifications/native_notification_delegate.h" | |
| 26 #include "chrome/browser/notifications/notification.h" | 25 #include "chrome/browser/notifications/notification.h" |
| 27 #include "chrome/browser/notifications/notification_common.h" | 26 #include "chrome/browser/notifications/notification_common.h" |
| 28 #include "chrome/browser/notifications/notification_delegate.h" | 27 #include "chrome/browser/notifications/notification_delegate.h" |
| 29 #include "chrome/browser/notifications/notifier_state_tracker.h" | 28 #include "chrome/browser/notifications/notifier_state_tracker.h" |
| 30 #include "chrome/browser/notifications/notifier_state_tracker_factory.h" | 29 #include "chrome/browser/notifications/notifier_state_tracker_factory.h" |
| 30 #include "chrome/browser/notifications/web_notification_delegate.h" |
| 31 #include "chrome/browser/profiles/profile.h" | 31 #include "chrome/browser/profiles/profile.h" |
| 32 #include "chrome/common/chrome_features.h" | |
| 33 #include "chrome/common/extensions/api/notifications/notification_style.h" | 32 #include "chrome/common/extensions/api/notifications/notification_style.h" |
| 34 #include "chrome/common/features.h" | |
| 35 #include "components/keyed_service/content/browser_context_keyed_service_shutdow
n_notifier_factory.h" | 33 #include "components/keyed_service/content/browser_context_keyed_service_shutdow
n_notifier_factory.h" |
| 36 #include "components/keyed_service/core/keyed_service_shutdown_notifier.h" | 34 #include "components/keyed_service/core/keyed_service_shutdown_notifier.h" |
| 37 #include "content/public/browser/render_process_host.h" | 35 #include "content/public/browser/render_process_host.h" |
| 38 #include "content/public/browser/render_view_host.h" | 36 #include "content/public/browser/render_view_host.h" |
| 39 #include "content/public/browser/web_contents.h" | 37 #include "content/public/browser/web_contents.h" |
| 40 #include "extensions/browser/app_window/app_window.h" | 38 #include "extensions/browser/app_window/app_window.h" |
| 41 #include "extensions/browser/app_window/app_window_registry.h" | 39 #include "extensions/browser/app_window/app_window_registry.h" |
| 42 #include "extensions/browser/app_window/native_app_window.h" | 40 #include "extensions/browser/app_window/native_app_window.h" |
| 43 #include "extensions/browser/event_router.h" | 41 #include "extensions/browser/event_router.h" |
| 44 #include "extensions/browser/extension_system_provider.h" | 42 #include "extensions/browser/extension_system_provider.h" |
| 45 #include "extensions/browser/extensions_browser_client.h" | 43 #include "extensions/browser/extensions_browser_client.h" |
| 46 #include "extensions/common/extension.h" | 44 #include "extensions/common/extension.h" |
| 47 #include "extensions/common/features/feature.h" | |
| 48 #include "third_party/skia/include/core/SkBitmap.h" | 45 #include "third_party/skia/include/core/SkBitmap.h" |
| 49 #include "ui/base/layout.h" | 46 #include "ui/base/layout.h" |
| 50 #include "ui/gfx/image/image.h" | 47 #include "ui/gfx/image/image.h" |
| 51 #include "ui/gfx/image/image_skia.h" | 48 #include "ui/gfx/image/image_skia.h" |
| 52 #include "ui/gfx/image/image_skia_operations.h" | 49 #include "ui/gfx/image/image_skia_operations.h" |
| 53 #include "ui/gfx/image/image_skia_rep.h" | 50 #include "ui/gfx/image/image_skia_rep.h" |
| 54 #include "ui/gfx/skia_util.h" | 51 #include "ui/gfx/skia_util.h" |
| 55 #include "ui/message_center/message_center_style.h" | 52 #include "ui/message_center/message_center_style.h" |
| 56 #include "ui/message_center/notifier_settings.h" | 53 #include "ui/message_center/notifier_settings.h" |
| 57 #include "url/gurl.h" | 54 #include "url/gurl.h" |
| 58 | 55 |
| 59 using message_center::NotifierId; | 56 using message_center::NotifierId; |
| 60 | 57 |
| 61 namespace extensions { | 58 namespace extensions { |
| 62 | 59 |
| 63 namespace notifications = api::notifications; | 60 namespace notifications = api::notifications; |
| 64 | 61 |
| 65 const base::Feature kAllowFullscreenAppNotificationsFeature{ | |
| 66 "FSNotificationsApp", base::FEATURE_ENABLED_BY_DEFAULT | |
| 67 }; | |
| 68 | |
| 69 namespace { | 62 namespace { |
| 70 | 63 |
| 71 const char kMissingRequiredPropertiesForCreateNotification[] = | 64 const char kMissingRequiredPropertiesForCreateNotification[] = |
| 72 "Some of the required properties are missing: type, iconUrl, title and " | 65 "Some of the required properties are missing: type, iconUrl, title and " |
| 73 "message."; | 66 "message."; |
| 74 const char kUnableToDecodeIconError[] = | 67 const char kUnableToDecodeIconError[] = |
| 75 "Unable to successfully use the provided image."; | 68 "Unable to successfully use the provided image."; |
| 76 const char kUnexpectedProgressValueForNonProgressType[] = | 69 const char kUnexpectedProgressValueForNonProgressType[] = |
| 77 "The progress value should not be specified for non-progress notification"; | 70 "The progress value should not be specified for non-progress notification"; |
| 78 const char kInvalidProgressValue[] = | 71 const char kInvalidProgressValue[] = |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 ((c_rgba_data[rgba_index + 2] & 0xFF) << 0)); | 176 ((c_rgba_data[rgba_index + 2] & 0xFF) << 0)); |
| 184 } | 177 } |
| 185 | 178 |
| 186 // TODO(dewittj): Handle HiDPI images with more than one scale factor | 179 // TODO(dewittj): Handle HiDPI images with more than one scale factor |
| 187 // representation. | 180 // representation. |
| 188 gfx::ImageSkia skia(gfx::ImageSkiaRep(bitmap, 1.0f)); | 181 gfx::ImageSkia skia(gfx::ImageSkiaRep(bitmap, 1.0f)); |
| 189 *return_image = gfx::Image(skia); | 182 *return_image = gfx::Image(skia); |
| 190 return true; | 183 return true; |
| 191 } | 184 } |
| 192 | 185 |
| 193 class ShutdownNotifierFactory | |
| 194 : public BrowserContextKeyedServiceShutdownNotifierFactory { | |
| 195 public: | |
| 196 static ShutdownNotifierFactory* GetInstance() { | |
| 197 return base::Singleton<ShutdownNotifierFactory>::get(); | |
| 198 } | |
| 199 | |
| 200 private: | |
| 201 friend struct base::DefaultSingletonTraits<ShutdownNotifierFactory>; | |
| 202 | |
| 203 ShutdownNotifierFactory() | |
| 204 : BrowserContextKeyedServiceShutdownNotifierFactory( | |
| 205 "NotificationsApiDelegate") { | |
| 206 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); | |
| 207 } | |
| 208 ~ShutdownNotifierFactory() override {} | |
| 209 | |
| 210 DISALLOW_COPY_AND_ASSIGN(ShutdownNotifierFactory); | |
| 211 }; | |
| 212 | |
| 213 // Message center based notification delegate with all the functionality. | |
| 214 class NotificationApiDelegate : public NotificationDelegate { | |
| 215 public: | |
| 216 NotificationApiDelegate(ChromeAsyncExtensionFunction* api_function, | |
| 217 Profile* profile, | |
| 218 const std::string& extension_id, | |
| 219 const std::string& id) | |
| 220 : api_function_(api_function), | |
| 221 event_router_(EventRouter::Get(profile)), | |
| 222 display_helper_( | |
| 223 ExtensionNotificationDisplayHelperFactory::GetForProfile(profile)), | |
| 224 extension_id_(extension_id), | |
| 225 scoped_id_(CreateScopedIdentifier(extension_id, id)) { | |
| 226 DCHECK(api_function_); | |
| 227 DCHECK(display_helper_); | |
| 228 | |
| 229 shutdown_notifier_subscription_ = | |
| 230 ShutdownNotifierFactory::GetInstance()->Get(profile)->Subscribe( | |
| 231 base::Bind(&NotificationApiDelegate::Shutdown, | |
| 232 base::Unretained(this))); | |
| 233 } | |
| 234 | |
| 235 void Close(bool by_user) override { | |
| 236 EventRouter::UserGestureState gesture = | |
| 237 by_user ? EventRouter::USER_GESTURE_ENABLED | |
| 238 : EventRouter::USER_GESTURE_NOT_ENABLED; | |
| 239 std::unique_ptr<base::ListValue> args(CreateBaseEventArgs()); | |
| 240 args->AppendBoolean(by_user); | |
| 241 SendEvent(events::NOTIFICATIONS_ON_CLOSED, | |
| 242 notifications::OnClosed::kEventName, gesture, std::move(args)); | |
| 243 | |
| 244 DCHECK(display_helper_); | |
| 245 display_helper_->EraseDataForNotificationId(scoped_id_); | |
| 246 } | |
| 247 | |
| 248 void Click() override { | |
| 249 std::unique_ptr<base::ListValue> args(CreateBaseEventArgs()); | |
| 250 SendEvent(events::NOTIFICATIONS_ON_CLICKED, | |
| 251 notifications::OnClicked::kEventName, | |
| 252 EventRouter::USER_GESTURE_ENABLED, std::move(args)); | |
| 253 } | |
| 254 | |
| 255 bool HasClickedListener() override { | |
| 256 if (!event_router_) | |
| 257 return false; | |
| 258 | |
| 259 return event_router_->HasEventListener( | |
| 260 notifications::OnClicked::kEventName); | |
| 261 } | |
| 262 | |
| 263 void ButtonClick(int index) override { | |
| 264 std::unique_ptr<base::ListValue> args(CreateBaseEventArgs()); | |
| 265 args->AppendInteger(index); | |
| 266 SendEvent(events::NOTIFICATIONS_ON_BUTTON_CLICKED, | |
| 267 notifications::OnButtonClicked::kEventName, | |
| 268 EventRouter::USER_GESTURE_ENABLED, std::move(args)); | |
| 269 } | |
| 270 | |
| 271 std::string id() const override { return scoped_id_; } | |
| 272 | |
| 273 // Should only display when fullscreen if this app is the source of the | |
| 274 // fullscreen window. | |
| 275 bool ShouldDisplayOverFullscreen() const override { | |
| 276 AppWindowRegistry::AppWindowList windows = AppWindowRegistry::Get( | |
| 277 api_function_->GetProfile())->GetAppWindowsForApp(extension_id_); | |
| 278 for (auto* window : windows) { | |
| 279 // Window must be fullscreen and visible | |
| 280 if (window->IsFullscreen() && window->GetBaseWindow()->IsActive()) { | |
| 281 bool enabled = base::FeatureList::IsEnabled( | |
| 282 kAllowFullscreenAppNotificationsFeature); | |
| 283 if (enabled) { | |
| 284 UMA_HISTOGRAM_ENUMERATION("Notifications.Display_Fullscreen.Shown", | |
| 285 NotifierId::APPLICATION, | |
| 286 NotifierId::SIZE); | |
| 287 } else { | |
| 288 UMA_HISTOGRAM_ENUMERATION( | |
| 289 "Notifications.Display_Fullscreen.Suppressed", | |
| 290 NotifierId::APPLICATION, | |
| 291 NotifierId::SIZE); | |
| 292 | |
| 293 } | |
| 294 return enabled; | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 return false; | |
| 299 } | |
| 300 | |
| 301 private: | |
| 302 ~NotificationApiDelegate() override {} | |
| 303 | |
| 304 void SendEvent(events::HistogramValue histogram_value, | |
| 305 const std::string& name, | |
| 306 EventRouter::UserGestureState user_gesture, | |
| 307 std::unique_ptr<base::ListValue> args) { | |
| 308 if (!event_router_) | |
| 309 return; | |
| 310 | |
| 311 std::unique_ptr<Event> event( | |
| 312 new Event(histogram_value, name, std::move(args))); | |
| 313 event->user_gesture = user_gesture; | |
| 314 event_router_->DispatchEventToExtension(extension_id_, std::move(event)); | |
| 315 } | |
| 316 | |
| 317 void Shutdown() { | |
| 318 shutdown_notifier_subscription_.reset(); | |
| 319 event_router_ = nullptr; | |
| 320 display_helper_ = nullptr; | |
| 321 } | |
| 322 | |
| 323 std::unique_ptr<base::ListValue> CreateBaseEventArgs() { | |
| 324 std::unique_ptr<base::ListValue> args(new base::ListValue()); | |
| 325 args->AppendString(StripScopeFromIdentifier(extension_id_, scoped_id_)); | |
| 326 return args; | |
| 327 } | |
| 328 | |
| 329 scoped_refptr<ChromeAsyncExtensionFunction> api_function_; | |
| 330 | |
| 331 // Since this class is refcounted it may outlive the profile. We listen for | |
| 332 // profile-keyed service shutdown events and reset to nullptr at that time, | |
| 333 // so make sure to check for a valid pointer before use. | |
| 334 EventRouter* event_router_; | |
| 335 ExtensionNotificationDisplayHelper* display_helper_; | |
| 336 | |
| 337 const std::string extension_id_; | |
| 338 const std::string scoped_id_; | |
| 339 | |
| 340 std::unique_ptr<KeyedServiceShutdownNotifier::Subscription> | |
| 341 shutdown_notifier_subscription_; | |
| 342 | |
| 343 DISALLOW_COPY_AND_ASSIGN(NotificationApiDelegate); | |
| 344 }; | |
| 345 | |
| 346 } // namespace | 186 } // namespace |
| 347 | 187 |
| 348 bool NotificationsApiFunction::IsNotificationsApiAvailable() { | 188 bool NotificationsApiFunction::IsNotificationsApiAvailable() { |
| 349 // We need to check this explicitly rather than letting | 189 // We need to check this explicitly rather than letting |
| 350 // _permission_features.json enforce it, because we're sharing the | 190 // _permission_features.json enforce it, because we're sharing the |
| 351 // chrome.notifications permissions namespace with WebKit notifications. | 191 // chrome.notifications permissions namespace with WebKit notifications. |
| 352 return extension()->is_platform_app() || extension()->is_extension(); | 192 return extension()->is_platform_app() || extension()->is_extension(); |
| 353 } | 193 } |
| 354 | 194 |
| 355 NotificationsApiFunction::NotificationsApiFunction() { | 195 NotificationsApiFunction::NotificationsApiFunction() { |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 490 optional_fields.items.push_back(message_center::NotificationItem( | 330 optional_fields.items.push_back(message_center::NotificationItem( |
| 491 base::UTF8ToUTF16(api_item.title), | 331 base::UTF8ToUTF16(api_item.title), |
| 492 base::UTF8ToUTF16(api_item.message))); | 332 base::UTF8ToUTF16(api_item.message))); |
| 493 } | 333 } |
| 494 } | 334 } |
| 495 | 335 |
| 496 if (options->is_clickable.get()) | 336 if (options->is_clickable.get()) |
| 497 optional_fields.clickable = *options->is_clickable; | 337 optional_fields.clickable = *options->is_clickable; |
| 498 | 338 |
| 499 // Create the notification api delegate. Ownership passed to the notification. | 339 // Create the notification api delegate. Ownership passed to the notification. |
| 500 NotificationDelegate* api_delegate; | 340 NotificationDelegate* api_delegate = new WebNotificationDelegate( |
| 501 #if BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS) | 341 NotificationCommon::EXTENSION, GetProfile(), |
| 502 if (base::FeatureList::IsEnabled(features::kNativeNotifications) && | 342 CreateScopedIdentifier(extension_->id(), id), extension_->url()); |
| 503 g_browser_process->notification_platform_bridge()) { | |
| 504 api_delegate = new NativeNotificationDelegate( | |
| 505 CreateScopedIdentifier(extension_->id(), id)); | |
| 506 } else { | |
| 507 api_delegate = | |
| 508 new NotificationApiDelegate(this, GetProfile(), extension_->id(), id); | |
| 509 } | |
| 510 #else | |
| 511 api_delegate = | |
| 512 new NotificationApiDelegate(this, GetProfile(), extension_->id(), id); | |
| 513 #endif // BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS) | |
| 514 | 343 |
| 515 Notification notification( | 344 Notification notification( |
| 516 type, title, message, icon, | 345 type, title, message, icon, |
| 517 message_center::NotifierId(message_center::NotifierId::APPLICATION, | 346 message_center::NotifierId(message_center::NotifierId::APPLICATION, |
| 518 extension_->id()), | 347 extension_->id()), |
| 519 base::UTF8ToUTF16(extension_->name()), extension_->url(), | 348 base::UTF8ToUTF16(extension_->name()), extension_->url(), |
| 520 api_delegate->id(), optional_fields, api_delegate); | 349 api_delegate->id(), optional_fields, api_delegate); |
| 521 | 350 |
| 522 // Apply the "requireInteraction" flag. The value defaults to false. | 351 // Apply the "requireInteraction" flag. The value defaults to false. |
| 523 notification.set_never_timeout(options->require_interaction && | 352 notification.set_never_timeout(options->require_interaction && |
| (...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 848 : api::notifications::PERMISSION_LEVEL_DENIED; | 677 : api::notifications::PERMISSION_LEVEL_DENIED; |
| 849 | 678 |
| 850 SetResult( | 679 SetResult( |
| 851 base::MakeUnique<base::Value>(api::notifications::ToString(result))); | 680 base::MakeUnique<base::Value>(api::notifications::ToString(result))); |
| 852 SendResponse(true); | 681 SendResponse(true); |
| 853 | 682 |
| 854 return true; | 683 return true; |
| 855 } | 684 } |
| 856 | 685 |
| 857 } // namespace extensions | 686 } // namespace extensions |
| OLD | NEW |