1/*
2 * Copyright (C) 2011, 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 "WebNotificationManagerProxy.h"
28
29#include "APIArray.h"
30#include "APINotificationProvider.h"
31#include "APISecurityOrigin.h"
32#include "WebNotification.h"
33#include "WebNotificationManagerMessages.h"
34#include "WebPageProxy.h"
35#include "WebProcessPool.h"
36#include "WebProcessProxy.h"
37
38namespace WebKit {
39using namespace WebCore;
40
41static uint64_t generateGlobalNotificationID()
42{
43 static uint64_t uniqueGlobalNotificationID = 1;
44 return uniqueGlobalNotificationID++;
45}
46
47const char* WebNotificationManagerProxy::supplementName()
48{
49 return "WebNotificationManagerProxy";
50}
51
52Ref<WebNotificationManagerProxy> WebNotificationManagerProxy::create(WebProcessPool* processPool)
53{
54 return adoptRef(*new WebNotificationManagerProxy(processPool));
55}
56
57WebNotificationManagerProxy::WebNotificationManagerProxy(WebProcessPool* processPool)
58 : WebContextSupplement(processPool)
59 , m_provider(std::make_unique<API::NotificationProvider>())
60{
61}
62
63void WebNotificationManagerProxy::setProvider(std::unique_ptr<API::NotificationProvider>&& provider)
64{
65 if (!provider) {
66 m_provider = std::make_unique<API::NotificationProvider>();
67 return;
68 }
69
70 m_provider = WTFMove(provider);
71 m_provider->addNotificationManager(*this);
72}
73
74// WebContextSupplement
75
76void WebNotificationManagerProxy::processPoolDestroyed()
77{
78 m_provider->removeNotificationManager(*this);
79}
80
81void WebNotificationManagerProxy::refWebContextSupplement()
82{
83 API::Object::ref();
84}
85
86void WebNotificationManagerProxy::derefWebContextSupplement()
87{
88 API::Object::deref();
89}
90
91HashMap<String, bool> WebNotificationManagerProxy::notificationPermissions()
92{
93 return m_provider->notificationPermissions();
94}
95
96void WebNotificationManagerProxy::show(WebPageProxy* webPage, const String& title, const String& body, const String& iconURL, const String& tag, const String& lang, WebCore::NotificationDirection dir, const String& originString, uint64_t pageNotificationID)
97{
98 uint64_t globalNotificationID = generateGlobalNotificationID();
99 auto notification = WebNotification::create(title, body, iconURL, tag, lang, dir, originString, globalNotificationID);
100 std::pair<PageIdentifier, uint64_t> notificationIDPair = std::make_pair(webPage->pageID(), pageNotificationID);
101 m_globalNotificationMap.set(globalNotificationID, notificationIDPair);
102 m_notifications.set(notificationIDPair, std::make_pair(globalNotificationID, notification.copyRef()));
103 m_provider->show(*webPage, notification.get());
104}
105
106void WebNotificationManagerProxy::cancel(WebPageProxy* webPage, uint64_t pageNotificationID)
107{
108 if (WebNotification* notification = m_notifications.get(std::make_pair(webPage->pageID(), pageNotificationID)).second.get())
109 m_provider->cancel(*notification);
110}
111
112void WebNotificationManagerProxy::didDestroyNotification(WebPageProxy* webPage, uint64_t pageNotificationID)
113{
114 auto globalIDNotificationPair = m_notifications.take(std::make_pair(webPage->pageID(), pageNotificationID));
115 if (uint64_t globalNotificationID = globalIDNotificationPair.first) {
116 WebNotification* notification = globalIDNotificationPair.second.get();
117 m_globalNotificationMap.remove(globalNotificationID);
118 m_provider->didDestroyNotification(*notification);
119 }
120}
121
122static bool pageIDsMatch(PageIdentifier pageID, uint64_t, PageIdentifier desiredPageID, const Vector<uint64_t>&)
123{
124 return pageID == desiredPageID;
125}
126
127static bool pageAndNotificationIDsMatch(PageIdentifier pageID, uint64_t pageNotificationID, PageIdentifier desiredPageID, const Vector<uint64_t>& desiredPageNotificationIDs)
128{
129 return pageID == desiredPageID && desiredPageNotificationIDs.contains(pageNotificationID);
130}
131
132void WebNotificationManagerProxy::clearNotifications(WebPageProxy* webPage)
133{
134 clearNotifications(webPage, Vector<uint64_t>(), pageIDsMatch);
135}
136
137void WebNotificationManagerProxy::clearNotifications(WebPageProxy* webPage, const Vector<uint64_t>& pageNotificationIDs)
138{
139 clearNotifications(webPage, pageNotificationIDs, pageAndNotificationIDsMatch);
140}
141
142void WebNotificationManagerProxy::clearNotifications(WebPageProxy* webPage, const Vector<uint64_t>& pageNotificationIDs, NotificationFilterFunction filterFunction)
143{
144 auto targetPageID = webPage->pageID();
145
146 Vector<uint64_t> globalNotificationIDs;
147 globalNotificationIDs.reserveCapacity(m_globalNotificationMap.size());
148
149 for (auto it = m_notifications.begin(), end = m_notifications.end(); it != end; ++it) {
150 auto webPageID = it->key.first;
151 uint64_t pageNotificationID = it->key.second;
152 if (!filterFunction(webPageID, pageNotificationID, targetPageID, pageNotificationIDs))
153 continue;
154
155 uint64_t globalNotificationID = it->value.first;
156 globalNotificationIDs.append(globalNotificationID);
157 }
158
159 for (auto it = globalNotificationIDs.begin(), end = globalNotificationIDs.end(); it != end; ++it) {
160 auto pageNotification = m_globalNotificationMap.take(*it);
161 m_notifications.remove(pageNotification);
162 }
163
164 m_provider->clearNotifications(globalNotificationIDs);
165}
166
167void WebNotificationManagerProxy::providerDidShowNotification(uint64_t globalNotificationID)
168{
169 auto it = m_globalNotificationMap.find(globalNotificationID);
170 if (it == m_globalNotificationMap.end())
171 return;
172
173 auto webPageID = it->value.first;
174 WebPageProxy* webPage = WebProcessProxy::webPage(webPageID);
175 if (!webPage)
176 return;
177
178 uint64_t pageNotificationID = it->value.second;
179 webPage->process().send(Messages::WebNotificationManager::DidShowNotification(pageNotificationID), 0);
180}
181
182void WebNotificationManagerProxy::providerDidClickNotification(uint64_t globalNotificationID)
183{
184 auto it = m_globalNotificationMap.find(globalNotificationID);
185 if (it == m_globalNotificationMap.end())
186 return;
187
188 auto webPageID = it->value.first;
189 WebPageProxy* webPage = WebProcessProxy::webPage(webPageID);
190 if (!webPage)
191 return;
192
193 uint64_t pageNotificationID = it->value.second;
194 webPage->process().send(Messages::WebNotificationManager::DidClickNotification(pageNotificationID), 0);
195}
196
197
198void WebNotificationManagerProxy::providerDidCloseNotifications(API::Array* globalNotificationIDs)
199{
200 HashMap<WebPageProxy*, Vector<uint64_t>> pageNotificationIDs;
201
202 size_t size = globalNotificationIDs->size();
203 for (size_t i = 0; i < size; ++i) {
204 auto it = m_globalNotificationMap.find(globalNotificationIDs->at<API::UInt64>(i)->value());
205 if (it == m_globalNotificationMap.end())
206 continue;
207
208 if (WebPageProxy* webPage = WebProcessProxy::webPage(it->value.first)) {
209 auto pageIt = pageNotificationIDs.find(webPage);
210 if (pageIt == pageNotificationIDs.end()) {
211 Vector<uint64_t> newVector;
212 newVector.reserveInitialCapacity(size);
213 pageIt = pageNotificationIDs.add(webPage, WTFMove(newVector)).iterator;
214 }
215
216 uint64_t pageNotificationID = it->value.second;
217 pageIt->value.append(pageNotificationID);
218 }
219
220 m_notifications.remove(it->value);
221 m_globalNotificationMap.remove(it);
222 }
223
224 for (auto it = pageNotificationIDs.begin(), end = pageNotificationIDs.end(); it != end; ++it)
225 it->key->process().send(Messages::WebNotificationManager::DidCloseNotifications(it->value), 0);
226}
227
228void WebNotificationManagerProxy::providerDidUpdateNotificationPolicy(const API::SecurityOrigin* origin, bool allowed)
229{
230 if (!processPool())
231 return;
232
233 processPool()->sendToAllProcesses(Messages::WebNotificationManager::DidUpdateNotificationDecision(origin->securityOrigin().toString(), allowed));
234}
235
236void WebNotificationManagerProxy::providerDidRemoveNotificationPolicies(API::Array* origins)
237{
238 if (!processPool())
239 return;
240
241 size_t size = origins->size();
242 if (!size)
243 return;
244
245 Vector<String> originStrings;
246 originStrings.reserveInitialCapacity(size);
247
248 for (size_t i = 0; i < size; ++i)
249 originStrings.append(origins->at<API::SecurityOrigin>(i)->securityOrigin().toString());
250
251 processPool()->sendToAllProcesses(Messages::WebNotificationManager::DidRemoveNotificationDecisions(originStrings));
252}
253
254uint64_t WebNotificationManagerProxy::notificationLocalIDForTesting(WebNotification* notification)
255{
256 if (!notification)
257 return 0;
258
259 auto it = m_globalNotificationMap.find(notification->notificationID());
260 if (it == m_globalNotificationMap.end())
261 return 0;
262
263 return it->value.second;
264}
265
266} // namespace WebKit
267