1/*
2 * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebNotificationManager.h"
28
29#include "WebCoreArgumentCoders.h"
30#include "WebPage.h"
31#include "WebProcess.h"
32#include "WebProcessCreationParameters.h"
33
34#if ENABLE(NOTIFICATIONS)
35#include "WebNotification.h"
36#include "WebNotificationManagerMessages.h"
37#include "WebPageProxyMessages.h"
38#include <WebCore/Document.h>
39#include <WebCore/Notification.h>
40#include <WebCore/Page.h>
41#include <WebCore/ScriptExecutionContext.h>
42#include <WebCore/SecurityOrigin.h>
43#include <WebCore/Settings.h>
44#include <WebCore/UserGestureIndicator.h>
45#endif
46
47namespace WebKit {
48using namespace WebCore;
49
50#if ENABLE(NOTIFICATIONS)
51static uint64_t generateNotificationID()
52{
53 static uint64_t uniqueNotificationID = 1;
54 return uniqueNotificationID++;
55}
56#endif
57
58const char* WebNotificationManager::supplementName()
59{
60 return "WebNotificationManager";
61}
62
63WebNotificationManager::WebNotificationManager(WebProcess& process)
64 : m_process(process)
65{
66#if ENABLE(NOTIFICATIONS)
67 m_process.addMessageReceiver(Messages::WebNotificationManager::messageReceiverName(), *this);
68#endif
69}
70
71WebNotificationManager::~WebNotificationManager()
72{
73}
74
75void WebNotificationManager::initialize(const WebProcessCreationParameters& parameters)
76{
77#if ENABLE(NOTIFICATIONS)
78 m_permissionsMap = parameters.notificationPermissions;
79#else
80 UNUSED_PARAM(parameters);
81#endif
82}
83
84void WebNotificationManager::didUpdateNotificationDecision(const String& originString, bool allowed)
85{
86#if ENABLE(NOTIFICATIONS)
87 m_permissionsMap.set(originString, allowed);
88#else
89 UNUSED_PARAM(originString);
90 UNUSED_PARAM(allowed);
91#endif
92}
93
94void WebNotificationManager::didRemoveNotificationDecisions(const Vector<String>& originStrings)
95{
96#if ENABLE(NOTIFICATIONS)
97 for (auto& originString : originStrings)
98 m_permissionsMap.remove(originString);
99#else
100 UNUSED_PARAM(originStrings);
101#endif
102}
103
104NotificationClient::Permission WebNotificationManager::policyForOrigin(WebCore::SecurityOrigin* origin) const
105{
106#if ENABLE(NOTIFICATIONS)
107 if (!origin)
108 return NotificationClient::Permission::Default;
109
110 ASSERT(!origin->isUnique());
111 auto it = m_permissionsMap.find(origin->toRawString());
112 if (it != m_permissionsMap.end())
113 return it->value ? NotificationClient::Permission::Granted : NotificationClient::Permission::Denied;
114#else
115 UNUSED_PARAM(origin);
116#endif
117
118 return NotificationClient::Permission::Default;
119}
120
121void WebNotificationManager::removeAllPermissionsForTesting()
122{
123#if ENABLE(NOTIFICATIONS)
124 m_permissionsMap.clear();
125#endif
126}
127
128uint64_t WebNotificationManager::notificationIDForTesting(Notification* notification)
129{
130#if ENABLE(NOTIFICATIONS)
131 if (!notification)
132 return 0;
133 return m_notificationMap.get(notification);
134#else
135 UNUSED_PARAM(notification);
136 return 0;
137#endif
138}
139
140bool WebNotificationManager::show(Notification* notification, WebPage* page)
141{
142#if ENABLE(NOTIFICATIONS)
143 if (!notification || !page->corePage()->settings().notificationsEnabled())
144 return false;
145
146 uint64_t notificationID = generateNotificationID();
147 m_notificationMap.set(notification, notificationID);
148 m_notificationIDMap.set(notificationID, notification);
149
150 auto it = m_notificationContextMap.add(notification->scriptExecutionContext(), Vector<uint64_t>()).iterator;
151 it->value.append(notificationID);
152
153 m_process.parentProcessConnection()->send(Messages::WebPageProxy::ShowNotification(notification->title(), notification->body(), notification->icon().string(), notification->tag(), notification->lang(), notification->dir(), notification->scriptExecutionContext()->securityOrigin()->toString(), notificationID), page->pageID());
154 return true;
155#else
156 UNUSED_PARAM(notification);
157 UNUSED_PARAM(page);
158 return false;
159#endif
160}
161
162void WebNotificationManager::cancel(Notification* notification, WebPage* page)
163{
164#if ENABLE(NOTIFICATIONS)
165 if (!notification || !page->corePage()->settings().notificationsEnabled())
166 return;
167
168 uint64_t notificationID = m_notificationMap.get(notification);
169 if (!notificationID)
170 return;
171
172 m_process.parentProcessConnection()->send(Messages::WebPageProxy::CancelNotification(notificationID), page->pageID());
173#else
174 UNUSED_PARAM(notification);
175 UNUSED_PARAM(page);
176#endif
177}
178
179void WebNotificationManager::clearNotifications(WebCore::ScriptExecutionContext* context, WebPage* page)
180{
181#if ENABLE(NOTIFICATIONS)
182 NotificationContextMap::iterator it = m_notificationContextMap.find(context);
183 if (it == m_notificationContextMap.end())
184 return;
185
186 Vector<uint64_t>& notificationIDs = it->value;
187 m_process.parentProcessConnection()->send(Messages::WebPageProxy::ClearNotifications(notificationIDs), page->pageID());
188 size_t count = notificationIDs.size();
189 for (size_t i = 0; i < count; ++i) {
190 RefPtr<Notification> notification = m_notificationIDMap.take(notificationIDs[i]);
191 if (!notification)
192 continue;
193 notification->finalize();
194 m_notificationMap.remove(notification);
195 }
196
197 m_notificationContextMap.remove(it);
198#else
199 UNUSED_PARAM(context);
200 UNUSED_PARAM(page);
201#endif
202}
203
204void WebNotificationManager::didDestroyNotification(Notification* notification, WebPage* page)
205{
206#if ENABLE(NOTIFICATIONS)
207 uint64_t notificationID = m_notificationMap.take(notification);
208 if (!notificationID)
209 return;
210
211 m_notificationIDMap.remove(notificationID);
212 removeNotificationFromContextMap(notificationID, notification);
213 m_process.parentProcessConnection()->send(Messages::WebPageProxy::DidDestroyNotification(notificationID), page->pageID());
214#else
215 UNUSED_PARAM(notification);
216 UNUSED_PARAM(page);
217#endif
218}
219
220void WebNotificationManager::didShowNotification(uint64_t notificationID)
221{
222#if ENABLE(NOTIFICATIONS)
223 if (!isNotificationIDValid(notificationID))
224 return;
225
226 RefPtr<Notification> notification = m_notificationIDMap.get(notificationID);
227 if (!notification)
228 return;
229
230 notification->dispatchShowEvent();
231#else
232 UNUSED_PARAM(notificationID);
233#endif
234}
235
236void WebNotificationManager::didClickNotification(uint64_t notificationID)
237{
238#if ENABLE(NOTIFICATIONS)
239 if (!isNotificationIDValid(notificationID))
240 return;
241
242 RefPtr<Notification> notification = m_notificationIDMap.get(notificationID);
243 if (!notification)
244 return;
245
246 // Indicate that this event is being dispatched in reaction to a user's interaction with a platform notification.
247 UserGestureIndicator indicator(ProcessingUserGesture);
248 notification->dispatchClickEvent();
249#else
250 UNUSED_PARAM(notificationID);
251#endif
252}
253
254void WebNotificationManager::didCloseNotifications(const Vector<uint64_t>& notificationIDs)
255{
256#if ENABLE(NOTIFICATIONS)
257 size_t count = notificationIDs.size();
258 for (size_t i = 0; i < count; ++i) {
259 uint64_t notificationID = notificationIDs[i];
260 if (!isNotificationIDValid(notificationID))
261 continue;
262
263 RefPtr<Notification> notification = m_notificationIDMap.take(notificationID);
264 if (!notification)
265 continue;
266
267 m_notificationMap.remove(notification);
268 removeNotificationFromContextMap(notificationID, notification.get());
269
270 notification->dispatchCloseEvent();
271 }
272#else
273 UNUSED_PARAM(notificationIDs);
274#endif
275}
276
277#if ENABLE(NOTIFICATIONS)
278void WebNotificationManager::removeNotificationFromContextMap(uint64_t notificationID, Notification* notification)
279{
280 // This is a helper function for managing the hash maps.
281 NotificationContextMap::iterator it = m_notificationContextMap.find(notification->scriptExecutionContext());
282 ASSERT(it != m_notificationContextMap.end());
283 size_t index = it->value.find(notificationID);
284 ASSERT(index != notFound);
285 it->value.remove(index);
286 if (it->value.isEmpty())
287 m_notificationContextMap.remove(it);
288}
289#endif
290
291} // namespace WebKit
292