blob: 741a087c07896c2423a076566c700f6b80524b94 [file] [log] [blame]
Peter Beverloo7426194d2017-12-11 17:39:381// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/notifications/notification_display_service_impl.h"
6
7#include <utility>
8
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/logging.h"
12#include "base/metrics/histogram_macros.h"
13#include "build/build_config.h"
14#include "build/buildflag.h"
15#include "chrome/browser/browser_process.h"
16#include "chrome/browser/notifications/non_persistent_notification_handler.h"
17#include "chrome/browser/notifications/notification_display_service_factory.h"
18#include "chrome/browser/notifications/notification_platform_bridge.h"
19#include "chrome/browser/notifications/persistent_notification_handler.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/common/chrome_features.h"
22#include "content/public/browser/browser_thread.h"
Evan Stade196ec042018-03-13 19:44:3623#include "ui/base/ui_base_features.h"
Evan Stade889ce4712018-01-28 15:26:2624#include "ui/message_center/public/cpp/notification.h"
Peter Beverloo7426194d2017-12-11 17:39:3825
26#if BUILDFLAG(ENABLE_EXTENSIONS)
27#include "chrome/browser/extensions/api/notifications/extension_notification_handler.h"
28#endif
29
30#if BUILDFLAG(ENABLE_MESSAGE_CENTER)
31#include "chrome/browser/notifications/notification_platform_bridge_message_center.h"
32#endif
33
34#if defined(OS_WIN)
35#include "base/strings/utf_string_conversions.h"
Xi Cheng23313de2018-02-24 04:15:3936#include "chrome/browser/notifications/notification_platform_bridge_win.h"
Peter Beverloo7426194d2017-12-11 17:39:3837#endif
38
39namespace {
40
41// Returns the NotificationPlatformBridge to use for the current platform.
42// Will return a nullptr for platforms that don't support native notifications.
43//
44// Platforms behave as follows:
45//
46// * Android
47// Always uses native notifications.
48//
49// * Mac OS X, Linux
50// Uses native notifications by default, but can fall back to the message
51// center if base::kNativeNotifications is disabled or initialization fails.
52//
53// * Windows 10 RS1+:
54// Uses the message center by default, but can use native notifications if
55// base::kNativeNotifications is enabled or initialization fails.
56//
57// * Chrome OS:
58// Always uses the message center, either through the message center
59// notification platform bridge when base::kNativeNotifications is disabled,
60// which means the message center runs in-process, or through the Chrome OS
61// specific bridge when the flag is enabled, which displays out-of-process.
62//
63// Please try to keep this comment up to date when changing behaviour on one of
64// the platforms supported by the browser.
65NotificationPlatformBridge* GetNativeNotificationPlatformBridge() {
66#if BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS)
67#if defined(OS_ANDROID)
68 DCHECK(base::FeatureList::IsEnabled(features::kNativeNotifications));
69 return g_browser_process->notification_platform_bridge();
70#elif defined(OS_WIN)
Xi Cheng23313de2018-02-24 04:15:3971 if (NotificationPlatformBridgeWin::NativeNotificationEnabled())
Peter Beverloo7426194d2017-12-11 17:39:3872 return g_browser_process->notification_platform_bridge();
Evan Stade196ec042018-03-13 19:44:3673#elif defined(OS_CHROMEOS)
Evan Stade4d8df162018-06-07 16:59:2574 return g_browser_process->notification_platform_bridge();
Peter Beverloo7426194d2017-12-11 17:39:3875#else
76 if (base::FeatureList::IsEnabled(features::kNativeNotifications) &&
77 g_browser_process->notification_platform_bridge()) {
78 return g_browser_process->notification_platform_bridge();
79 }
80#endif
81#endif // BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS)
82
83 // The platform does not support, or has not enabled, native notifications.
84 return nullptr;
85}
86
87// Returns the NotificationPlatformBridge to use for the message center. May be
88// a nullptr for platforms where the message center is not available.
89std::unique_ptr<NotificationPlatformBridge> CreateMessageCenterBridge(
90 Profile* profile) {
91#if BUILDFLAG(ENABLE_MESSAGE_CENTER)
92 return std::make_unique<NotificationPlatformBridgeMessageCenter>(profile);
93#else
94 return nullptr;
95#endif
96}
97
Peter Beverloo7426194d2017-12-11 17:39:3898void OperationCompleted() {
99 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
100}
101
102} // namespace
103
104// static
105NotificationDisplayServiceImpl* NotificationDisplayServiceImpl::GetForProfile(
106 Profile* profile) {
107 return static_cast<NotificationDisplayServiceImpl*>(
108 NotificationDisplayServiceFactory::GetForProfile(profile));
109}
110
111NotificationDisplayServiceImpl::NotificationDisplayServiceImpl(Profile* profile)
112 : profile_(profile),
113 message_center_bridge_(CreateMessageCenterBridge(profile)),
114 bridge_(GetNativeNotificationPlatformBridge()),
115 weak_factory_(this) {
116 // TODO(peter): Move these to the NotificationDisplayServiceFactory.
117 AddNotificationHandler(NotificationHandler::Type::WEB_NON_PERSISTENT,
118 std::make_unique<NonPersistentNotificationHandler>());
119 AddNotificationHandler(NotificationHandler::Type::WEB_PERSISTENT,
120 std::make_unique<PersistentNotificationHandler>());
121#if BUILDFLAG(ENABLE_EXTENSIONS)
122 AddNotificationHandler(
123 NotificationHandler::Type::EXTENSION,
124 std::make_unique<extensions::ExtensionNotificationHandler>());
125#endif
126
127 // Initialize the bridge if native notifications are available, otherwise
128 // signal that the bridge could not be initialized.
129 if (bridge_) {
130 bridge_->SetReadyCallback(base::BindOnce(
131 &NotificationDisplayServiceImpl::OnNotificationPlatformBridgeReady,
132 weak_factory_.GetWeakPtr()));
133 } else {
134 OnNotificationPlatformBridgeReady(false /* success */);
135 }
136}
137
138NotificationDisplayServiceImpl::~NotificationDisplayServiceImpl() = default;
139
140void NotificationDisplayServiceImpl::ProcessNotificationOperation(
141 NotificationCommon::Operation operation,
142 NotificationHandler::Type notification_type,
143 const GURL& origin,
144 const std::string& notification_id,
145 const base::Optional<int>& action_index,
146 const base::Optional<base::string16>& reply,
147 const base::Optional<bool>& by_user) {
148 NotificationHandler* handler = GetNotificationHandler(notification_type);
149 DCHECK(handler);
150 if (!handler) {
151 LOG(ERROR) << "Unable to find a handler for "
152 << static_cast<int>(notification_type);
153 return;
154 }
155
156 // TODO(crbug.com/766854): Plumb this through from the notification platform
157 // bridges so they can report completion of the operation as needed.
158 base::OnceClosure completed_closure = base::BindOnce(&OperationCompleted);
159
160 switch (operation) {
161 case NotificationCommon::CLICK:
162 handler->OnClick(profile_, origin, notification_id, action_index, reply,
163 std::move(completed_closure));
164 break;
165 case NotificationCommon::CLOSE:
166 DCHECK(by_user.has_value());
167 handler->OnClose(profile_, origin, notification_id, by_user.value(),
168 std::move(completed_closure));
169 break;
170 case NotificationCommon::DISABLE_PERMISSION:
171 handler->DisableNotifications(profile_, origin);
172 break;
173 case NotificationCommon::SETTINGS:
174 handler->OpenSettings(profile_, origin);
175 break;
176 }
177}
178
179void NotificationDisplayServiceImpl::AddNotificationHandler(
180 NotificationHandler::Type notification_type,
181 std::unique_ptr<NotificationHandler> handler) {
182 DCHECK(handler);
183 DCHECK_EQ(notification_handlers_.count(notification_type), 0u);
184 notification_handlers_[notification_type] = std::move(handler);
185}
186
187NotificationHandler* NotificationDisplayServiceImpl::GetNotificationHandler(
188 NotificationHandler::Type notification_type) {
189 auto found = notification_handlers_.find(notification_type);
190 if (found != notification_handlers_.end())
191 return found->second.get();
192 return nullptr;
193}
194
Peter Beverloo7426194d2017-12-11 17:39:38195void NotificationDisplayServiceImpl::Display(
196 NotificationHandler::Type notification_type,
197 const message_center::Notification& notification,
198 std::unique_ptr<NotificationCommon::Metadata> metadata) {
199 // TODO(estade): in the future, the reverse should also be true: a
200 // non-TRANSIENT type implies no delegate.
201 if (notification_type == NotificationHandler::Type::TRANSIENT)
202 DCHECK(notification.delegate());
203
204 if (!bridge_initialized_) {
205 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::Display,
206 weak_factory_.GetWeakPtr(), notification_type,
207 notification, std::move(metadata)));
208 return;
209 }
210
Joone Hur1fbcaf202018-04-16 21:04:38211#if BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS)
Peter Beverloo7426194d2017-12-11 17:39:38212 NotificationPlatformBridge* bridge =
213 NotificationPlatformBridge::CanHandleType(notification_type)
214 ? bridge_
215 : message_center_bridge_.get();
216 DCHECK(bridge);
217
Evan Stade257439232018-04-05 02:53:40218 bridge->Display(notification_type, profile_, notification,
Peter Beverloo7426194d2017-12-11 17:39:38219 std::move(metadata));
Joone Hur1fbcaf202018-04-16 21:04:38220#endif
Peter Beverloo7426194d2017-12-11 17:39:38221
222 NotificationHandler* handler = GetNotificationHandler(notification_type);
223 if (handler)
224 handler->OnShow(profile_, notification.id());
225}
226
227void NotificationDisplayServiceImpl::Close(
228 NotificationHandler::Type notification_type,
229 const std::string& notification_id) {
230 if (!bridge_initialized_) {
231 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::Close,
232 weak_factory_.GetWeakPtr(), notification_type,
233 notification_id));
234 return;
235 }
236
Joone Hur1fbcaf202018-04-16 21:04:38237#if BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS)
Peter Beverloo7426194d2017-12-11 17:39:38238 NotificationPlatformBridge* bridge =
239 NotificationPlatformBridge::CanHandleType(notification_type)
240 ? bridge_
241 : message_center_bridge_.get();
242 DCHECK(bridge);
243
Evan Stade257439232018-04-05 02:53:40244 bridge->Close(profile_, notification_id);
Joone Hur1fbcaf202018-04-16 21:04:38245#endif
Peter Beverloo7426194d2017-12-11 17:39:38246}
247
248void NotificationDisplayServiceImpl::GetDisplayed(
Finnur Thorarinsson0bfb7e72018-03-20 12:26:06249 DisplayedNotificationsCallback callback) {
Peter Beverloo7426194d2017-12-11 17:39:38250 if (!bridge_initialized_) {
251 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::GetDisplayed,
252 weak_factory_.GetWeakPtr(), callback));
253 return;
254 }
255
Evan Stade257439232018-04-05 02:53:40256 bridge_->GetDisplayed(profile_, std::move(callback));
Peter Beverloo7426194d2017-12-11 17:39:38257}
258
Daniel Bratell1f65ad72018-01-22 16:12:38259// Callback to run once the profile has been loaded in order to perform a
260// given |operation| in a notification.
261void NotificationDisplayServiceImpl::ProfileLoadedCallback(
262 NotificationCommon::Operation operation,
263 NotificationHandler::Type notification_type,
264 const GURL& origin,
265 const std::string& notification_id,
266 const base::Optional<int>& action_index,
267 const base::Optional<base::string16>& reply,
268 const base::Optional<bool>& by_user,
269 Profile* profile) {
270 if (!profile) {
271 // TODO(miguelg): Add UMA for this condition.
272 // Perhaps propagate this through PersistentNotificationStatus.
273 LOG(WARNING) << "Profile not loaded correctly";
274 return;
275 }
276
277 NotificationDisplayServiceImpl* display_service =
278 NotificationDisplayServiceImpl::GetForProfile(profile);
279 display_service->ProcessNotificationOperation(operation, notification_type,
280 origin, notification_id,
281 action_index, reply, by_user);
282}
283
Peter Beverloo7426194d2017-12-11 17:39:38284void NotificationDisplayServiceImpl::OnNotificationPlatformBridgeReady(
285 bool success) {
Finnur Thorarinssonbcd84b822018-03-12 17:44:49286 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Evan Stade4d8df162018-06-07 16:59:25287#if BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS) && !defined(OS_CHROMEOS)
Peter Beverloo7426194d2017-12-11 17:39:38288 if (base::FeatureList::IsEnabled(features::kNativeNotifications)) {
289 UMA_HISTOGRAM_BOOLEAN("Notifications.UsingNativeNotificationCenter",
290 success);
291 }
Joone Hur6dff914a2018-04-16 08:37:29292#endif
Peter Beverloo7426194d2017-12-11 17:39:38293
294 if (!success) {
295 // Fall back to the message center if initialization failed. Initialization
296 // must always succeed on platforms where the message center is unavailable.
297 DCHECK(message_center_bridge_);
298 bridge_ = message_center_bridge_.get();
299 }
300
301 bridge_initialized_ = true;
302
303 // Flush any pending actions that have yet to execute.
304 while (!actions_.empty()) {
305 std::move(actions_.front()).Run();
306 actions_.pop();
307 }
308}