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
38using namespace std;
39using namespace WTF;
40using namespace WebCore;
41
42namespace WebKit {
43
44static uint64_t generateGlobalNotificationID()
45{
46    static uint64_t uniqueGlobalNotificationID = 1;
47    return uniqueGlobalNotificationID++;
48}
49
50const char* WebNotificationManagerProxy::supplementName()
51{
52    return "WebNotificationManagerProxy";
53}
54
55PassRefPtr<WebNotificationManagerProxy> WebNotificationManagerProxy::create(WebContext* context)
56{
57    return adoptRef(new WebNotificationManagerProxy(context));
58}
59
60WebNotificationManagerProxy::WebNotificationManagerProxy(WebContext* context)
61    : WebContextSupplement(context)
62{
63}
64
65void WebNotificationManagerProxy::initializeProvider(const WKNotificationProvider *provider)
66{
67    m_provider.initialize(provider);
68    m_provider.addNotificationManager(this);
69}
70
71// WebContextSupplement
72
73void WebNotificationManagerProxy::contextDestroyed()
74{
75    m_provider.removeNotificationManager(this);
76}
77
78void WebNotificationManagerProxy::refWebContextSupplement()
79{
80    APIObject::ref();
81}
82
83void WebNotificationManagerProxy::derefWebContextSupplement()
84{
85    APIObject::deref();
86}
87
88void 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
103void 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
113void 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
119void 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
129static bool pageIDsMatch(uint64_t pageID, uint64_t, uint64_t desiredPageID, const Vector<uint64_t>&)
130{
131    return pageID == desiredPageID;
132}
133
134static 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
139void WebNotificationManagerProxy::clearNotifications(WebPageProxy* webPage)
140{
141    clearNotifications(webPage, Vector<uint64_t>(), pageIDsMatch);
142}
143
144void WebNotificationManagerProxy::clearNotifications(WebPageProxy* webPage, const Vector<uint64_t>& pageNotificationIDs)
145{
146    clearNotifications(webPage, pageNotificationIDs, pageAndNotificationIDsMatch);
147}
148
149void 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
174void 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
189void 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
205void 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
235void 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
243void 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
262