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