1/*
2 * Copyright (C) 2012 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 "WebNotificationProvider.h"
28
29#include <WebKit/WKMutableArray.h>
30#include <WebKit/WKNotification.h>
31#include <WebKit/WKNotificationManager.h>
32#include <WebKit/WKNumber.h>
33#include <WebKit/WKSecurityOriginRef.h>
34#include <wtf/Assertions.h>
35
36namespace WTR {
37
38static void showWebNotification(WKPageRef page, WKNotificationRef notification, const void* clientInfo)
39{
40 static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->showWebNotification(page, notification);
41}
42
43static void closeWebNotification(WKNotificationRef notification, const void* clientInfo)
44{
45 static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->closeWebNotification(notification);
46}
47
48static void addNotificationManager(WKNotificationManagerRef manager, const void* clientInfo)
49{
50 static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->addNotificationManager(manager);
51}
52
53static void removeNotificationManager(WKNotificationManagerRef manager, const void* clientInfo)
54{
55 static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->removeNotificationManager(manager);
56}
57
58static WKDictionaryRef notificationPermissions(const void* clientInfo)
59{
60 return static_cast<WebNotificationProvider*>(const_cast<void*>(clientInfo))->notificationPermissions();
61}
62
63WebNotificationProvider::WebNotificationProvider()
64{
65}
66
67WebNotificationProvider::~WebNotificationProvider()
68{
69 for (auto& manager : m_ownedNotifications)
70 WKNotificationManagerSetProvider(manager.key.get(), nullptr);
71}
72
73WKNotificationProviderV0 WebNotificationProvider::provider()
74{
75 WKNotificationProviderV0 notificationProvider = {
76 { 0, this },
77 WTR::showWebNotification,
78 WTR::closeWebNotification,
79 0, // didDestroyNotification
80 WTR::addNotificationManager,
81 WTR::removeNotificationManager,
82 WTR::notificationPermissions,
83 0, // clearNotifications
84 };
85 return notificationProvider;
86}
87
88void WebNotificationProvider::showWebNotification(WKPageRef page, WKNotificationRef notification)
89{
90 auto context = WKPageGetContext(page);
91 auto notificationManager = WKContextGetNotificationManager(context);
92 uint64_t id = WKNotificationGetID(notification);
93
94 ASSERT(m_ownedNotifications.contains(notificationManager));
95 auto addResult = m_ownedNotifications.find(notificationManager)->value.add(id);
96 ASSERT_UNUSED(addResult, addResult.isNewEntry);
97 auto addResult2 = m_owningManager.set(id, notificationManager);
98 ASSERT_UNUSED(addResult2, addResult2.isNewEntry);
99 auto addResult3 = m_localToGlobalNotificationIDMap.add(std::make_pair(page, WKNotificationManagerGetLocalIDForTesting(notificationManager, notification)), id);
100 ASSERT_UNUSED(addResult3, addResult3.isNewEntry);
101
102 WKNotificationManagerProviderDidShowNotification(notificationManager, id);
103}
104
105static void removeGlobalIDFromIDMap(HashMap<std::pair<WKPageRef, uint64_t>, uint64_t>& map, uint64_t id)
106{
107 for (auto iter = map.begin(); iter != map.end(); ++iter) {
108 if (iter->value == id) {
109 map.remove(iter);
110 return;
111 }
112 }
113 ASSERT_NOT_REACHED();
114}
115
116void WebNotificationProvider::closeWebNotification(WKNotificationRef notification)
117{
118 uint64_t id = WKNotificationGetID(notification);
119 ASSERT(m_owningManager.contains(id));
120 auto notificationManager = m_owningManager.get(id);
121
122 ASSERT(m_ownedNotifications.contains(notificationManager));
123 bool success = m_ownedNotifications.find(notificationManager)->value.remove(id);
124 ASSERT_UNUSED(success, success);
125 m_owningManager.remove(id);
126
127 removeGlobalIDFromIDMap(m_localToGlobalNotificationIDMap, id);
128
129 WKRetainPtr<WKUInt64Ref> wkID = adoptWK(WKUInt64Create(id));
130 WKRetainPtr<WKMutableArrayRef> array = adoptWK(WKMutableArrayCreate());
131 WKArrayAppendItem(array.get(), wkID.get());
132 WKNotificationManagerProviderDidCloseNotifications(notificationManager, array.get());
133}
134
135void WebNotificationProvider::addNotificationManager(WKNotificationManagerRef manager)
136{
137 m_ownedNotifications.add(manager, HashSet<uint64_t>());
138}
139
140void WebNotificationProvider::removeNotificationManager(WKNotificationManagerRef manager)
141{
142 auto iterator = m_ownedNotifications.find(manager);
143 ASSERT(iterator != m_ownedNotifications.end());
144 auto toRemove = iterator->value;
145 WKRetainPtr<WKNotificationManagerRef> guard(manager);
146 m_ownedNotifications.remove(iterator);
147 WKRetainPtr<WKMutableArrayRef> array = adoptWK(WKMutableArrayCreate());
148 for (uint64_t notificationID : toRemove) {
149 bool success = m_owningManager.remove(notificationID);
150 ASSERT_UNUSED(success, success);
151 removeGlobalIDFromIDMap(m_localToGlobalNotificationIDMap, notificationID);
152 WKArrayAppendItem(array.get(), adoptWK(WKUInt64Create(notificationID)).get());
153 }
154 WKNotificationManagerProviderDidCloseNotifications(manager, array.get());
155}
156
157WKDictionaryRef WebNotificationProvider::notificationPermissions()
158{
159 // Initial permissions are always empty.
160 return WKMutableDictionaryCreate();
161}
162
163void WebNotificationProvider::simulateWebNotificationClick(WKPageRef page, uint64_t notificationID)
164{
165 ASSERT(m_localToGlobalNotificationIDMap.contains(std::make_pair(page, notificationID)));
166 auto globalID = m_localToGlobalNotificationIDMap.get(std::make_pair(page, notificationID));
167 ASSERT(m_owningManager.contains(globalID));
168 WKNotificationManagerProviderDidClickNotification(m_owningManager.get(globalID), globalID);
169}
170
171void WebNotificationProvider::reset()
172{
173 for (auto& notificationPair : m_ownedNotifications) {
174 if (notificationPair.value.isEmpty())
175 continue;
176 WKRetainPtr<WKMutableArrayRef> array = adoptWK(WKMutableArrayCreate());
177 for (uint64_t notificationID : notificationPair.value)
178 WKArrayAppendItem(array.get(), adoptWK(WKUInt64Create(notificationID)).get());
179
180 notificationPair.value.clear();
181 WKNotificationManagerProviderDidCloseNotifications(notificationPair.key.get(), array.get());
182 }
183 m_owningManager.clear();
184 m_localToGlobalNotificationIDMap.clear();
185}
186
187} // namespace WTR
188