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) || ENABLE(LEGACY_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/BlockExceptions.h> 36#import <WebCore/Page.h> 37#import <WebCore/ScriptExecutionContext.h> 38#endif 39 40#if ENABLE(NOTIFICATIONS) 41#import <WebCore/NotificationPermissionCallback.h> 42#endif 43#if ENABLE(LEGACY_NOTIFICATIONS) 44#import <WebCore/VoidCallback.h> 45#endif 46 47using namespace WebCore; 48 49#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 50@interface WebNotificationPolicyListener : NSObject <WebAllowDenyPolicyListener> 51{ 52#if ENABLE(NOTIFICATIONS) 53 RefPtr<NotificationPermissionCallback> _callback; 54#endif 55#if ENABLE(LEGACY_NOTIFICATIONS) 56 RefPtr<VoidCallback> _voidCallback; 57 bool _isLegacyRequest; 58#endif 59} 60#if ENABLE(NOTIFICATIONS) 61- (id)initWithCallback:(PassRefPtr<NotificationPermissionCallback>)callback; 62#endif 63#if ENABLE(LEGACY_NOTIFICATIONS) 64- (id)initWithVoidCallback:(PassRefPtr<VoidCallback>)callback; 65#endif 66 67@end 68#endif 69 70#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 71static uint64_t generateNotificationID() 72{ 73 static uint64_t uniqueNotificationID = 1; 74 return uniqueNotificationID++; 75} 76#endif 77 78WebNotificationClient::WebNotificationClient(WebView *webView) 79 : m_webView(webView) 80{ 81} 82 83bool WebNotificationClient::show(Notification* notification) 84{ 85#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 86 if (![m_webView _notificationProvider]) 87 return false; 88 89 uint64_t notificationID = generateNotificationID(); 90 RetainPtr<WebNotification> webNotification = adoptNS([[WebNotification alloc] initWithCoreNotification:notification notificationID:notificationID]); 91 m_notificationMap.set(notification, webNotification); 92 93 NotificationContextMap::iterator it = m_notificationContextMap.add(notification->scriptExecutionContext(), Vector<RetainPtr<WebNotification> >()).iterator; 94 it->value.append(webNotification); 95 96 [[m_webView _notificationProvider] showNotification:webNotification.get() fromWebView:m_webView]; 97 return true; 98#else 99 UNUSED_PARAM(notification); 100 return false; 101#endif 102} 103 104void WebNotificationClient::cancel(Notification* notification) 105{ 106#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 107 WebNotification *webNotification = m_notificationMap.get(notification).get(); 108 if (!webNotification) 109 return; 110 111 [[m_webView _notificationProvider] cancelNotification:webNotification]; 112#else 113 UNUSED_PARAM(notification); 114#endif 115} 116 117void WebNotificationClient::clearNotifications(ScriptExecutionContext* context) 118{ 119#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 120 NotificationContextMap::iterator it = m_notificationContextMap.find(context); 121 if (it == m_notificationContextMap.end()) 122 return; 123 124 Vector<RetainPtr<WebNotification> >& webNotifications = it->value; 125 NSMutableArray *nsIDs = [NSMutableArray array]; 126 size_t count = webNotifications.size(); 127 for (size_t i = 0; i < count; ++i) { 128 WebNotification *webNotification = webNotifications[i].get(); 129 [nsIDs addObject:[NSNumber numberWithUnsignedLongLong:[webNotification notificationID]]]; 130 core(webNotification)->finalize(); 131 m_notificationMap.remove(core(webNotification)); 132 } 133 134 [[m_webView _notificationProvider] clearNotifications:nsIDs]; 135 m_notificationContextMap.remove(it); 136#else 137 UNUSED_PARAM(context); 138#endif 139} 140 141void WebNotificationClient::notificationObjectDestroyed(Notification* notification) 142{ 143#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 144 RetainPtr<WebNotification> webNotification = m_notificationMap.take(notification); 145 if (!webNotification) 146 return; 147 148 NotificationContextMap::iterator it = m_notificationContextMap.find(notification->scriptExecutionContext()); 149 ASSERT(it != m_notificationContextMap.end()); 150 size_t index = it->value.find(webNotification); 151 ASSERT(index != notFound); 152 it->value.remove(index); 153 if (it->value.isEmpty()) 154 m_notificationContextMap.remove(it); 155 156 [[m_webView _notificationProvider] notificationDestroyed:webNotification.get()]; 157#else 158 UNUSED_PARAM(notification); 159#endif 160} 161 162void WebNotificationClient::notificationControllerDestroyed() 163{ 164 delete this; 165} 166 167#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 168void WebNotificationClient::requestPermission(ScriptExecutionContext* context, WebNotificationPolicyListener *listener) 169{ 170 SEL selector = @selector(webView:decidePolicyForNotificationRequestFromOrigin:listener:); 171 if (![[m_webView UIDelegate] respondsToSelector:selector]) 172 return; 173 174 WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:context->securityOrigin()]; 175 176 CallUIDelegate(m_webView, selector, webOrigin, listener); 177 178 [webOrigin release]; 179} 180#endif 181 182#if ENABLE(LEGACY_NOTIFICATIONS) 183void WebNotificationClient::requestPermission(ScriptExecutionContext* context, PassRefPtr<VoidCallback> callback) 184{ 185 BEGIN_BLOCK_OBJC_EXCEPTIONS; 186 WebNotificationPolicyListener *listener = [[WebNotificationPolicyListener alloc] initWithVoidCallback:callback]; 187 requestPermission(context, listener); 188 [listener release]; 189 END_BLOCK_OBJC_EXCEPTIONS; 190} 191#endif 192 193#if ENABLE(NOTIFICATIONS) 194void WebNotificationClient::requestPermission(ScriptExecutionContext* context, PassRefPtr<NotificationPermissionCallback> callback) 195{ 196 BEGIN_BLOCK_OBJC_EXCEPTIONS; 197 WebNotificationPolicyListener *listener = [[WebNotificationPolicyListener alloc] initWithCallback:callback]; 198 requestPermission(context, listener); 199 [listener release]; 200 END_BLOCK_OBJC_EXCEPTIONS; 201} 202#endif 203 204NotificationClient::Permission WebNotificationClient::checkPermission(ScriptExecutionContext* context) 205{ 206#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 207 if (!context || !context->isDocument()) 208 return NotificationClient::PermissionDenied; 209 if (![[m_webView preferences] notificationsEnabled]) 210 return NotificationClient::PermissionDenied; 211 WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:context->securityOrigin()]; 212 WebNotificationPermission permission = [[m_webView _notificationProvider] policyForOrigin:webOrigin]; 213 [webOrigin release]; 214 switch (permission) { 215 case WebNotificationPermissionAllowed: 216 return NotificationClient::PermissionAllowed; 217 case WebNotificationPermissionDenied: 218 return NotificationClient::PermissionDenied; 219 case WebNotificationPermissionNotAllowed: 220 return NotificationClient::PermissionNotAllowed; 221 default: 222 return NotificationClient::PermissionNotAllowed; 223 } 224#else 225 UNUSED_PARAM(context); 226 return NotificationClient::PermissionDenied; 227#endif 228} 229 230#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 231uint64_t WebNotificationClient::notificationIDForTesting(WebCore::Notification* notification) 232{ 233 return [m_notificationMap.get(notification).get() notificationID]; 234} 235 236@implementation WebNotificationPolicyListener 237 238#if ENABLE(NOTIFICATIONS) 239- (id)initWithCallback:(PassRefPtr<NotificationPermissionCallback>)callback 240{ 241 if (!(self = [super init])) 242 return nil; 243 244 _callback = callback; 245 return self; 246} 247#endif 248 249#if ENABLE(LEGACY_NOTIFICATIONS) 250- (id)initWithVoidCallback:(PassRefPtr<VoidCallback>)callback 251{ 252 if (!(self = [super init])) 253 return nil; 254 255 _isLegacyRequest = true; 256 _voidCallback = callback; 257 return self; 258} 259#endif 260 261- (void)allow 262{ 263#if ENABLE(LEGACY_NOTIFICATIONS) 264 if (_isLegacyRequest) { 265 if (_voidCallback) 266 _voidCallback->handleEvent(); 267 return; 268 } 269#endif 270#if ENABLE(NOTIFICATIONS) 271 if (_callback) 272 _callback->handleEvent(Notification::permissionString(NotificationClient::PermissionAllowed)); 273#endif 274} 275 276- (void)deny 277{ 278#if ENABLE(LEGACY_NOTIFICATIONS) 279 if (_isLegacyRequest) { 280 if (_voidCallback) 281 _voidCallback->handleEvent(); 282 return; 283 } 284#endif 285#if ENABLE(NOTIFICATIONS) 286 if (_callback) 287 _callback->handleEvent(Notification::permissionString(NotificationClient::PermissionDenied)); 288#endif 289} 290 291@end 292#endif 293