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 |