Remove support for legacy Notifications
[WebKit-https.git] / Source / WebKit / mac / WebCoreSupport / WebNotificationClient.mm
1 /*
2  * Copyright (C) 2011, 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 #import "WebNotificationClient.h"
27
28 #if ENABLE(NOTIFICATIONS)
29 #import "WebDelegateImplementationCaching.h"
30 #import "WebNotificationInternal.h"
31 #import "WebPreferencesPrivate.h"
32 #import "WebSecurityOriginInternal.h"
33 #import "WebUIDelegatePrivate.h"
34 #import "WebViewInternal.h"
35 #import <WebCore/NotificationPermissionCallback.h>
36 #import <WebCore/ScriptExecutionContext.h>
37 #import <wtf/BlockObjCExceptions.h>
38 #endif
39
40 using namespace WebCore;
41
42 #if ENABLE(NOTIFICATIONS)
43 @interface WebNotificationPolicyListener : NSObject <WebAllowDenyPolicyListener>
44 {
45     RefPtr<NotificationPermissionCallback> _callback;
46 }
47 - (id)initWithCallback:(PassRefPtr<NotificationPermissionCallback>)callback;
48 @end
49 #endif
50
51 #if ENABLE(NOTIFICATIONS)
52 static uint64_t generateNotificationID()
53 {
54     static uint64_t uniqueNotificationID = 1;
55     return uniqueNotificationID++;
56 }
57 #endif
58
59 WebNotificationClient::WebNotificationClient(WebView *webView)
60     : m_webView(webView)
61 {
62 }
63
64 bool WebNotificationClient::show(Notification* notification)
65 {
66 #if ENABLE(NOTIFICATIONS)
67     if (![m_webView _notificationProvider])
68         return false;
69
70     uint64_t notificationID = generateNotificationID();
71     RetainPtr<WebNotification> webNotification = adoptNS([[WebNotification alloc] initWithCoreNotification:notification notificationID:notificationID]);
72     m_notificationMap.set(notification, webNotification);
73
74     auto it = m_notificationContextMap.add(notification->scriptExecutionContext(), Vector<RetainPtr<WebNotification>>()).iterator;
75     it->value.append(webNotification);
76
77     [[m_webView _notificationProvider] showNotification:webNotification.get() fromWebView:m_webView];
78     return true;
79 #else
80     UNUSED_PARAM(notification);
81     return false;
82 #endif
83 }
84
85 void WebNotificationClient::cancel(Notification* notification)
86 {
87 #if ENABLE(NOTIFICATIONS)
88     WebNotification *webNotification = m_notificationMap.get(notification).get();
89     if (!webNotification)
90         return;
91
92     [[m_webView _notificationProvider] cancelNotification:webNotification];
93 #else
94     UNUSED_PARAM(notification);
95 #endif
96 }
97
98 void WebNotificationClient::clearNotifications(ScriptExecutionContext* context)
99 {
100 #if ENABLE(NOTIFICATIONS)
101     auto it = m_notificationContextMap.find(context);
102     if (it == m_notificationContextMap.end())
103         return;
104     
105     Vector<RetainPtr<WebNotification>>& webNotifications = it->value;
106     NSMutableArray *nsIDs = [NSMutableArray array];
107     size_t count = webNotifications.size();
108     for (size_t i = 0; i < count; ++i) {
109         WebNotification *webNotification = webNotifications[i].get();
110         [nsIDs addObject:[NSNumber numberWithUnsignedLongLong:[webNotification notificationID]]];
111         core(webNotification)->finalize();
112         m_notificationMap.remove(core(webNotification));
113     }
114
115     [[m_webView _notificationProvider] clearNotifications:nsIDs];
116     m_notificationContextMap.remove(it);
117 #else
118     UNUSED_PARAM(context);
119 #endif
120 }
121
122 void WebNotificationClient::notificationObjectDestroyed(Notification* notification)
123 {
124 #if ENABLE(NOTIFICATIONS)
125     RetainPtr<WebNotification> webNotification = m_notificationMap.take(notification);
126     if (!webNotification)
127         return;
128
129     auto it = m_notificationContextMap.find(notification->scriptExecutionContext());
130     ASSERT(it != m_notificationContextMap.end());
131     size_t index = it->value.find(webNotification);
132     ASSERT(index != notFound);
133     it->value.remove(index);
134     if (it->value.isEmpty())
135         m_notificationContextMap.remove(it);
136
137     [[m_webView _notificationProvider] notificationDestroyed:webNotification.get()];
138 #else
139     UNUSED_PARAM(notification);
140 #endif
141 }
142
143 void WebNotificationClient::notificationControllerDestroyed()
144 {
145     delete this;
146 }
147
148 #if ENABLE(NOTIFICATIONS)
149 void WebNotificationClient::requestPermission(ScriptExecutionContext* context, WebNotificationPolicyListener *listener)
150 {
151     SEL selector = @selector(webView:decidePolicyForNotificationRequestFromOrigin:listener:);
152     if (![[m_webView UIDelegate] respondsToSelector:selector])
153         return;
154
155     m_everRequestedPermission = true;
156
157     WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:context->securityOrigin()];
158     
159     CallUIDelegate(m_webView, selector, webOrigin, listener);
160     
161     [webOrigin release];
162 }
163 #endif
164
165 bool WebNotificationClient::hasPendingPermissionRequests(ScriptExecutionContext*) const
166 {
167 #if ENABLE(NOTIFICATIONS)
168     // We know permission was requested but we don't know if the client responded. In this case, we play it
169     // safe and presume there is one pending so that ActiveDOMObjects don't get suspended.
170     return m_everRequestedPermission;
171 #else
172     return false;
173 #endif
174 }
175
176 #if ENABLE(NOTIFICATIONS)
177 void WebNotificationClient::requestPermission(ScriptExecutionContext* context, RefPtr<NotificationPermissionCallback>&& callback)
178 {
179     BEGIN_BLOCK_OBJC_EXCEPTIONS;
180     WebNotificationPolicyListener *listener = [[WebNotificationPolicyListener alloc] initWithCallback:callback];
181     requestPermission(context, listener);
182     [listener release];
183     END_BLOCK_OBJC_EXCEPTIONS;
184 }
185 #endif
186
187 NotificationClient::Permission WebNotificationClient::checkPermission(ScriptExecutionContext* context)
188 {
189 #if ENABLE(NOTIFICATIONS)
190     if (!context || !context->isDocument())
191         return NotificationClient::PermissionDenied;
192     if (![[m_webView preferences] notificationsEnabled])
193         return NotificationClient::PermissionDenied;
194     WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:context->securityOrigin()];
195     WebNotificationPermission permission = [[m_webView _notificationProvider] policyForOrigin:webOrigin];
196     [webOrigin release];
197     switch (permission) {
198         case WebNotificationPermissionAllowed:
199             return NotificationClient::PermissionAllowed;
200         case WebNotificationPermissionDenied:
201             return NotificationClient::PermissionDenied;
202         case WebNotificationPermissionNotAllowed:
203             return NotificationClient::PermissionNotAllowed;
204         default:
205             return NotificationClient::PermissionNotAllowed;
206     }
207 #else
208     UNUSED_PARAM(context);
209     return NotificationClient::PermissionDenied;
210 #endif
211 }
212
213 #if ENABLE(NOTIFICATIONS)
214
215 uint64_t WebNotificationClient::notificationIDForTesting(WebCore::Notification* notification)
216 {
217     return [m_notificationMap.get(notification).get() notificationID];
218 }
219
220 @implementation WebNotificationPolicyListener
221
222 - (id)initWithCallback:(PassRefPtr<NotificationPermissionCallback>)callback
223 {
224     if (!(self = [super init]))
225         return nil;
226
227     _callback = callback;
228     return self;
229 }
230
231 - (void)allow
232 {
233     if (_callback)
234         _callback->handleEvent(Notification::permissionString(NotificationClient::PermissionAllowed));
235 }
236
237 - (void)deny
238 {
239     if (_callback)
240         _callback->handleEvent(Notification::permissionString(NotificationClient::PermissionDenied));
241 }
242
243 #if PLATFORM(IOS)
244 - (void)denyOnlyThisRequest
245 {
246     ASSERT_NOT_REACHED();
247 }
248
249 - (BOOL)shouldClearCache
250 {
251     ASSERT_NOT_REACHED();
252     return NO;
253 }
254 #endif
255
256 @end
257 #endif