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