1/*
2 * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
3 * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "NotificationManager.h"
29
30#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
31#include "UUID.h"
32#include "UserGestureIndicator.h"
33#include "WebPage_p.h"
34
35#include <BlackBerryPlatformString.h>
36
37#include <vector>
38
39using namespace WebCore;
40
41namespace BlackBerry {
42namespace WebKit {
43
44NotificationManager::NotificationManager(WebPagePrivate* webPagePrivate)
45    : m_webPagePrivate(webPagePrivate)
46{
47}
48
49NotificationManager::~NotificationManager()
50{
51}
52
53bool NotificationManager::show(Notification* notification)
54{
55    String notificationID = createCanonicalUUIDString();
56    m_notificationMap.set(notification, notificationID);
57    m_notificationIDMap.set(notificationID, notification);
58
59    NotificationContextMap::iterator it = m_notificationContextMap.add(notification->scriptExecutionContext(), Vector<String>()).iterator;
60    it->value.append(notificationID);
61
62    m_webPagePrivate->client()->showNotification(notificationID, notification->title(), notification->body(), notification->iconURL().string(), notification->tag(), notification->scriptExecutionContext()->securityOrigin()->toString());
63    return true;
64}
65
66void NotificationManager::cancel(Notification* notification)
67{
68    String notificationID = m_notificationMap.get(notification);
69    if (!notificationID)
70        return;
71
72    m_webPagePrivate->client()->cancelNotification(notificationID);
73}
74
75void NotificationManager::clearNotifications(ScriptExecutionContext* context)
76{
77    NotificationContextMap::iterator it = m_notificationContextMap.find(context);
78    if (it == m_notificationContextMap.end())
79        return;
80
81    Vector<String>& notificationIDs = it->value;
82    std::vector<BlackBerry::Platform::String> ids;
83    size_t count = notificationIDs.size();
84    for (size_t i = 0; i < count; ++i) {
85        ids.push_back(notificationIDs[i]);
86        RefPtr<Notification> notification = m_notificationIDMap.take(notificationIDs[i]);
87        if (!notification)
88            continue;
89
90        notification->finalize();
91        m_notificationMap.remove(notification);
92    }
93
94    m_webPagePrivate->client()->clearNotifications(ids);
95    m_notificationContextMap.remove(it);
96}
97
98void NotificationManager::notificationObjectDestroyed(Notification* notification)
99{
100    String notificationID = m_notificationMap.take(notification);
101    if (!notificationID)
102        return;
103
104    m_notificationIDMap.remove(notificationID);
105    removeNotificationFromContextMap(notificationID, notification);
106    m_webPagePrivate->client()->notificationDestroyed(notificationID);
107}
108
109#if ENABLE(LEGACY_NOTIFICATIONS)
110void NotificationManager::requestPermission(ScriptExecutionContext* context, PassRefPtr<VoidCallback> callback)
111{
112    SecurityOrigin* origin = context->securityOrigin();
113    String requestID = createCanonicalUUIDString();
114    m_originToIDMap.set(origin, requestID);
115    m_idToOriginMap.set(requestID, origin);
116    m_idToVoidCallbackMap.set(requestID, callback);
117    m_webPagePrivate->client()->requestNotificationPermission(requestID, origin->toString());
118}
119#endif
120
121#if ENABLE(NOTIFICATIONS)
122void NotificationManager::requestPermission(ScriptExecutionContext* context, PassRefPtr<NotificationPermissionCallback> callback)
123{
124    SecurityOrigin* origin = context->securityOrigin();
125    String requestID = createCanonicalUUIDString();
126    m_originToIDMap.set(origin, requestID);
127    m_idToOriginMap.set(requestID, origin);
128    m_idToCallbackMap.set(requestID, callback);
129    m_webPagePrivate->client()->requestNotificationPermission(requestID, origin->toString());
130}
131#endif
132
133void NotificationManager::cancelRequestsForPermission(ScriptExecutionContext* context)
134{
135    SecurityOrigin* origin = context->securityOrigin();
136    String requestID = m_originToIDMap.take(origin);
137    if (!requestID)
138        return;
139
140    m_idToOriginMap.remove(requestID);
141#if ENABLE(LEGACY_NOTIFICATIONS)
142    m_idToVoidCallbackMap.remove(requestID);
143#endif
144#if ENABLE(NOTIFICATIONS)
145    m_idToCallbackMap.remove(requestID);
146#endif
147}
148
149NotificationClient::Permission NotificationManager::checkPermission(ScriptExecutionContext* context)
150{
151    return static_cast<NotificationClient::Permission>(m_webPagePrivate->client()->checkNotificationPermission(context->securityOrigin()->toString()));
152}
153
154void NotificationManager::updatePermission(const String& requestID, bool allowed)
155{
156    RefPtr<WebCore::SecurityOrigin> origin = m_idToOriginMap.take(requestID);
157    m_originToIDMap.remove(origin);
158
159#if ENABLE(LEGACY_NOTIFICATIONS)
160    RefPtr<VoidCallback> voidCallback = m_idToVoidCallbackMap.take(requestID);
161    if (voidCallback) {
162        voidCallback->handleEvent();
163        return;
164    }
165#endif
166
167#if ENABLE(NOTIFICATIONS)
168    RefPtr<NotificationPermissionCallback> callback = m_idToCallbackMap.take(requestID);
169    if (!callback)
170        return;
171
172    callback->handleEvent(Notification::permissionString(allowed ? NotificationClient::PermissionAllowed : NotificationClient::PermissionDenied));
173#endif
174}
175
176void NotificationManager::notificationClicked(const String& notificationID)
177{
178    RefPtr<Notification> notification = m_notificationIDMap.get(notificationID);
179    if (!notification)
180        return;
181
182    // Indicate that this event is being dispatched in reaction to a user's interaction with a platform notification.
183    UserGestureIndicator indicator(DefinitelyProcessingNewUserGesture);
184    notification->dispatchClickEvent();
185}
186
187void NotificationManager::notificationClosed(const String& notificationID)
188{
189    RefPtr<Notification> notification = m_notificationIDMap.take(notificationID);
190    if (!notification)
191        return;
192
193    m_notificationMap.remove(notification);
194    removeNotificationFromContextMap(notificationID, notification.get());
195    notification->dispatchCloseEvent();
196}
197
198void NotificationManager::notificationError(const String& notificationID)
199{
200    RefPtr<Notification> notification = m_notificationIDMap.take(notificationID);
201    if (!notification)
202        return;
203
204    notification->dispatchErrorEvent();
205}
206
207void NotificationManager::notificationShown(const String& notificationID)
208{
209    RefPtr<Notification> notification = m_notificationIDMap.get(notificationID);
210    if (!notification)
211        return;
212
213    notification->dispatchShowEvent();
214}
215
216void NotificationManager::removeNotificationFromContextMap(const String& notificationID, Notification* notification)
217{
218    // This is a helper function for managing the hash maps.
219    NotificationContextMap::iterator it = m_notificationContextMap.find(notification->scriptExecutionContext());
220    ASSERT(it != m_notificationContextMap.end());
221    size_t index = it->value.find(notificationID);
222    ASSERT(index != notFound);
223    it->value.remove(index);
224    if (it->value.isEmpty())
225        m_notificationContextMap.remove(it);
226}
227
228}
229}
230
231#endif // ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
232