blob: ba21ab7033d5a839b5333ee767457413508c3f46 [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)
74 if (base::FeatureList::IsEnabled(features::kNativeNotifications) ||
75 base::FeatureList::IsEnabled(features::kMash)) {
76 return g_browser_process->notification_platform_bridge();
77 }
Peter Beverloo7426194d2017-12-11 17:39:3878#else
79 if (base::FeatureList::IsEnabled(features::kNativeNotifications) &&
80 g_browser_process->notification_platform_bridge()) {
81 return g_browser_process->notification_platform_bridge();
82 }
83#endif
84#endif // BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS)
85
86 // The platform does not support, or has not enabled, native notifications.
87 return nullptr;
88}
89
90// Returns the NotificationPlatformBridge to use for the message center. May be
91// a nullptr for platforms where the message center is not available.
92std::unique_ptr<NotificationPlatformBridge> CreateMessageCenterBridge(
93 Profile* profile) {
94#if BUILDFLAG(ENABLE_MESSAGE_CENTER)
95 return std::make_unique<NotificationPlatformBridgeMessageCenter>(profile);
96#else
97 return nullptr;
98#endif
99}
100
Peter Beverloo7426194d2017-12-11 17:39:38101void OperationCompleted() {
102 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
103}
104
105} // namespace
106
107// static
108NotificationDisplayServiceImpl* NotificationDisplayServiceImpl::GetForProfile(
109 Profile* profile) {
110 return static_cast<NotificationDisplayServiceImpl*>(
111 NotificationDisplayServiceFactory::GetForProfile(profile));
112}
113
114NotificationDisplayServiceImpl::NotificationDisplayServiceImpl(Profile* profile)
115 : profile_(profile),
116 message_center_bridge_(CreateMessageCenterBridge(profile)),
117 bridge_(GetNativeNotificationPlatformBridge()),
118 weak_factory_(this) {
119 // TODO(peter): Move these to the NotificationDisplayServiceFactory.
120 AddNotificationHandler(NotificationHandler::Type::WEB_NON_PERSISTENT,
121 std::make_unique<NonPersistentNotificationHandler>());
122 AddNotificationHandler(NotificationHandler::Type::WEB_PERSISTENT,
123 std::make_unique<PersistentNotificationHandler>());
124#if BUILDFLAG(ENABLE_EXTENSIONS)
125 AddNotificationHandler(
126 NotificationHandler::Type::EXTENSION,
127 std::make_unique<extensions::ExtensionNotificationHandler>());
128#endif
129
130 // Initialize the bridge if native notifications are available, otherwise
131 // signal that the bridge could not be initialized.
132 if (bridge_) {
133 bridge_->SetReadyCallback(base::BindOnce(
134 &NotificationDisplayServiceImpl::OnNotificationPlatformBridgeReady,
135 weak_factory_.GetWeakPtr()));
136 } else {
137 OnNotificationPlatformBridgeReady(false /* success */);
138 }
139}
140
141NotificationDisplayServiceImpl::~NotificationDisplayServiceImpl() = default;
142
143void NotificationDisplayServiceImpl::ProcessNotificationOperation(
144 NotificationCommon::Operation operation,
145 NotificationHandler::Type notification_type,
146 const GURL& origin,
147 const std::string& notification_id,
148 const base::Optional<int>& action_index,
149 const base::Optional<base::string16>& reply,
150 const base::Optional<bool>& by_user) {
151 NotificationHandler* handler = GetNotificationHandler(notification_type);
152 DCHECK(handler);
153 if (!handler) {
154 LOG(ERROR) << "Unable to find a handler for "
155 << static_cast<int>(notification_type);
156 return;
157 }
158
159 // TODO(crbug.com/766854): Plumb this through from the notification platform
160 // bridges so they can report completion of the operation as needed.
161 base::OnceClosure completed_closure = base::BindOnce(&OperationCompleted);
162
163 switch (operation) {
164 case NotificationCommon::CLICK:
165 handler->OnClick(profile_, origin, notification_id, action_index, reply,
166 std::move(completed_closure));
167 break;
168 case NotificationCommon::CLOSE:
169 DCHECK(by_user.has_value());
170 handler->OnClose(profile_, origin, notification_id, by_user.value(),
171 std::move(completed_closure));
172 break;
173 case NotificationCommon::DISABLE_PERMISSION:
174 handler->DisableNotifications(profile_, origin);
175 break;
176 case NotificationCommon::SETTINGS:
177 handler->OpenSettings(profile_, origin);
178 break;
179 }
180}
181
182void NotificationDisplayServiceImpl::AddNotificationHandler(
183 NotificationHandler::Type notification_type,
184 std::unique_ptr<NotificationHandler> handler) {
185 DCHECK(handler);
186 DCHECK_EQ(notification_handlers_.count(notification_type), 0u);
187 notification_handlers_[notification_type] = std::move(handler);
188}
189
190NotificationHandler* NotificationDisplayServiceImpl::GetNotificationHandler(
191 NotificationHandler::Type notification_type) {
192 auto found = notification_handlers_.find(notification_type);
193 if (found != notification_handlers_.end())
194 return found->second.get();
195 return nullptr;
196}
197
Peter Beverloo7426194d2017-12-11 17:39:38198void NotificationDisplayServiceImpl::Display(
199 NotificationHandler::Type notification_type,
200 const message_center::Notification& notification,
201 std::unique_ptr<NotificationCommon::Metadata> metadata) {
202 // TODO(estade): in the future, the reverse should also be true: a
203 // non-TRANSIENT type implies no delegate.
204 if (notification_type == NotificationHandler::Type::TRANSIENT)
205 DCHECK(notification.delegate());
206
207 if (!bridge_initialized_) {
208 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::Display,
209 weak_factory_.GetWeakPtr(), notification_type,
210 notification, std::move(metadata)));
211 return;
212 }
213
214 NotificationPlatformBridge* bridge =
215 NotificationPlatformBridge::CanHandleType(notification_type)
216 ? bridge_
217 : message_center_bridge_.get();
218 DCHECK(bridge);
219
Evan Stade257439232018-04-05 02:53:40220 bridge->Display(notification_type, profile_, notification,
Peter Beverloo7426194d2017-12-11 17:39:38221 std::move(metadata));
222
223 NotificationHandler* handler = GetNotificationHandler(notification_type);
224 if (handler)
225 handler->OnShow(profile_, notification.id());
226}
227
228void NotificationDisplayServiceImpl::Close(
229 NotificationHandler::Type notification_type,
230 const std::string& notification_id) {
231 if (!bridge_initialized_) {
232 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::Close,
233 weak_factory_.GetWeakPtr(), notification_type,
234 notification_id));
235 return;
236 }
237
238 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);
Peter Beverloo7426194d2017-12-11 17:39:38245}
246
247void NotificationDisplayServiceImpl::GetDisplayed(
Finnur Thorarinsson0bfb7e72018-03-20 12:26:06248 DisplayedNotificationsCallback callback) {
Peter Beverloo7426194d2017-12-11 17:39:38249 if (!bridge_initialized_) {
250 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::GetDisplayed,
251 weak_factory_.GetWeakPtr(), callback));
252 return;
253 }
254
Evan Stade257439232018-04-05 02:53:40255 bridge_->GetDisplayed(profile_, std::move(callback));
Peter Beverloo7426194d2017-12-11 17:39:38256}
257
Daniel Bratell1f65ad72018-01-22 16:12:38258// Callback to run once the profile has been loaded in order to perform a
259// given |operation| in a notification.
260void NotificationDisplayServiceImpl::ProfileLoadedCallback(
261 NotificationCommon::Operation operation,
262 NotificationHandler::Type notification_type,
263 const GURL& origin,
264 const std::string& notification_id,
265 const base::Optional<int>& action_index,
266 const base::Optional<base::string16>& reply,
267 const base::Optional<bool>& by_user,
268 Profile* profile) {
269 if (!profile) {
270 // TODO(miguelg): Add UMA for this condition.
271 // Perhaps propagate this through PersistentNotificationStatus.
272 LOG(WARNING) << "Profile not loaded correctly";
273 return;
274 }
275
276 NotificationDisplayServiceImpl* display_service =
277 NotificationDisplayServiceImpl::GetForProfile(profile);
278 display_service->ProcessNotificationOperation(operation, notification_type,
279 origin, notification_id,
280 action_index, reply, by_user);
281}
282
Peter Beverloo7426194d2017-12-11 17:39:38283void NotificationDisplayServiceImpl::OnNotificationPlatformBridgeReady(
284 bool success) {
Finnur Thorarinssonbcd84b822018-03-12 17:44:49285 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Joone Hur6dff914a2018-04-16 08:37:29286#if BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS)
Peter Beverloo7426194d2017-12-11 17:39:38287 if (base::FeatureList::IsEnabled(features::kNativeNotifications)) {
288 UMA_HISTOGRAM_BOOLEAN("Notifications.UsingNativeNotificationCenter",
289 success);
290 }
Joone Hur6dff914a2018-04-16 08:37:29291#endif
Peter Beverloo7426194d2017-12-11 17:39:38292
293 if (!success) {
294 // Fall back to the message center if initialization failed. Initialization
295 // must always succeed on platforms where the message center is unavailable.
296 DCHECK(message_center_bridge_);
297 bridge_ = message_center_bridge_.get();
298 }
299
300 bridge_initialized_ = true;
301
302 // Flush any pending actions that have yet to execute.
303 while (!actions_.empty()) {
304 std::move(actions_.front()).Run();
305 actions_.pop();
306 }
307}