1 /* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * Copyright (C) 2009, 2011, 2012 Apple Inc. 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 are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "config.h" 33 34 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 35 36 #include "modules/notifications/Notification.h" 37 38 #include "bindings/v8/Dictionary.h" 39 #include "bindings/v8/ExceptionState.h" 40 #include "core/dom/Document.h" 41 #include "core/dom/ErrorEvent.h" 42 #include "core/dom/EventNames.h" 43 #include "core/loader/ThreadableLoader.h" 44 #include "core/page/DOMWindow.h" 45 #include "core/page/WindowFocusAllowedIndicator.h" 46 #include "core/platform/network/ResourceRequest.h" 47 #include "core/platform/network/ResourceResponse.h" 48 #include "core/workers/WorkerGlobalScope.h" 49 #include "modules/notifications/DOMWindowNotifications.h" 50 #include "modules/notifications/NotificationCenter.h" 51 #include "modules/notifications/NotificationClient.h" 52 #include "modules/notifications/NotificationController.h" 53 #include "modules/notifications/NotificationPermissionCallback.h" 54 55 namespace WebCore { 56 57 Notification::Notification() 58 : ActiveDOMObject(0) 59 { 60 ScriptWrappable::init(this); 61 } 62 63 #if ENABLE(LEGACY_NOTIFICATIONS) 64 Notification::Notification(const String& title, const String& body, const String& iconURI, ScriptExecutionContext* context, ExceptionState& es, PassRefPtr<NotificationCenter> provider) 65 : ActiveDOMObject(context) 66 , m_title(title) 67 , m_body(body) 68 , m_state(Idle) 69 , m_notificationCenter(provider) 70 { 71 ScriptWrappable::init(this); 72 if (m_notificationCenter->checkPermission() != NotificationClient::PermissionAllowed) { 73 es.throwDOMException(SecurityError); 74 return; 75 } 76 77 m_icon = iconURI.isEmpty() ? KURL() : scriptExecutionContext()->completeURL(iconURI); 78 if (!m_icon.isEmpty() && !m_icon.isValid()) { 79 es.throwDOMException(SyntaxError); 80 return; 81 } 82 } 83 #endif 84 85 #if ENABLE(NOTIFICATIONS) 86 Notification::Notification(ScriptExecutionContext* context, const String& title) 87 : ActiveDOMObject(context) 88 , m_title(title) 89 , m_state(Idle) 90 , m_taskTimer(adoptPtr(new Timer<Notification>(this, &Notification::taskTimerFired))) 91 { 92 ScriptWrappable::init(this); 93 m_notificationCenter = DOMWindowNotifications::webkitNotifications(toDocument(context)->domWindow()); 94 95 ASSERT(m_notificationCenter->client()); 96 m_taskTimer->startOneShot(0); 97 } 98 #endif 99 100 Notification::~Notification() 101 { 102 } 103 104 #if ENABLE(LEGACY_NOTIFICATIONS) 105 PassRefPtr<Notification> Notification::create(const String& title, const String& body, const String& iconURI, ScriptExecutionContext* context, ExceptionState& es, PassRefPtr<NotificationCenter> provider) 106 { 107 RefPtr<Notification> notification(adoptRef(new Notification(title, body, iconURI, context, es, provider))); 108 notification->suspendIfNeeded(); 109 return notification.release(); 110 } 111 #endif 112 113 #if ENABLE(NOTIFICATIONS) 114 PassRefPtr<Notification> Notification::create(ScriptExecutionContext* context, const String& title, const Dictionary& options) 115 { 116 RefPtr<Notification> notification(adoptRef(new Notification(context, title))); 117 String argument; 118 if (options.get("body", argument)) 119 notification->setBody(argument); 120 if (options.get("tag", argument)) 121 notification->setTag(argument); 122 if (options.get("lang", argument)) 123 notification->setLang(argument); 124 if (options.get("dir", argument)) 125 notification->setDir(argument); 126 if (options.get("icon", argument)) { 127 KURL iconURI = argument.isEmpty() ? KURL() : context->completeURL(argument); 128 if (!iconURI.isEmpty() && iconURI.isValid()) 129 notification->setIconURL(iconURI); 130 } 131 132 notification->suspendIfNeeded(); 133 return notification.release(); 134 } 135 #endif 136 137 const AtomicString& Notification::interfaceName() const 138 { 139 return eventNames().interfaceForNotification; 140 } 141 142 void Notification::show() 143 { 144 // prevent double-showing 145 if (m_state == Idle && m_notificationCenter->client()) { 146 #if ENABLE(NOTIFICATIONS) 147 if (!toDocument(scriptExecutionContext())->page()) 148 return; 149 if (NotificationController::from(toDocument(scriptExecutionContext())->page())->client()->checkPermission(scriptExecutionContext()) != NotificationClient::PermissionAllowed) { 150 dispatchErrorEvent(); 151 return; 152 } 153 #endif 154 if (m_notificationCenter->client()->show(this)) { 155 m_state = Showing; 156 setPendingActivity(this); 157 } 158 } 159 } 160 161 void Notification::close() 162 { 163 switch (m_state) { 164 case Idle: 165 break; 166 case Showing: 167 if (m_notificationCenter->client()) 168 m_notificationCenter->client()->cancel(this); 169 break; 170 case Closed: 171 break; 172 } 173 } 174 175 EventTargetData* Notification::eventTargetData() 176 { 177 return &m_eventTargetData; 178 } 179 180 EventTargetData* Notification::ensureEventTargetData() 181 { 182 return &m_eventTargetData; 183 } 184 185 void Notification::contextDestroyed() 186 { 187 ActiveDOMObject::contextDestroyed(); 188 if (m_notificationCenter->client()) 189 m_notificationCenter->client()->notificationObjectDestroyed(this); 190 } 191 192 void Notification::finalize() 193 { 194 if (m_state == Closed) 195 return; 196 m_state = Closed; 197 unsetPendingActivity(this); 198 } 199 200 void Notification::dispatchShowEvent() 201 { 202 dispatchEvent(Event::create(eventNames().showEvent, false, false)); 203 } 204 205 void Notification::dispatchClickEvent() 206 { 207 WindowFocusAllowedIndicator windowFocusAllowed; 208 dispatchEvent(Event::create(eventNames().clickEvent, false, false)); 209 } 210 211 void Notification::dispatchCloseEvent() 212 { 213 dispatchEvent(Event::create(eventNames().closeEvent, false, false)); 214 finalize(); 215 } 216 217 void Notification::dispatchErrorEvent() 218 { 219 dispatchEvent(Event::create(eventNames().errorEvent, false, false)); 220 } 221 222 #if ENABLE(NOTIFICATIONS) 223 void Notification::taskTimerFired(Timer<Notification>* timer) 224 { 225 ASSERT(scriptExecutionContext()->isDocument()); 226 ASSERT_UNUSED(timer, timer == m_taskTimer.get()); 227 show(); 228 } 229 #endif 230 231 232 #if ENABLE(NOTIFICATIONS) 233 const String& Notification::permission(ScriptExecutionContext* context) 234 { 235 ASSERT(toDocument(context)->page()); 236 return permissionString(NotificationController::from(toDocument(context)->page())->client()->checkPermission(context)); 237 } 238 239 const String& Notification::permissionString(NotificationClient::Permission permission) 240 { 241 DEFINE_STATIC_LOCAL(const String, allowedPermission, ("granted")); 242 DEFINE_STATIC_LOCAL(const String, deniedPermission, ("denied")); 243 DEFINE_STATIC_LOCAL(const String, defaultPermission, ("default")); 244 245 switch (permission) { 246 case NotificationClient::PermissionAllowed: 247 return allowedPermission; 248 case NotificationClient::PermissionDenied: 249 return deniedPermission; 250 case NotificationClient::PermissionNotAllowed: 251 return defaultPermission; 252 } 253 254 ASSERT_NOT_REACHED(); 255 return deniedPermission; 256 } 257 258 void Notification::requestPermission(ScriptExecutionContext* context, PassRefPtr<NotificationPermissionCallback> callback) 259 { 260 ASSERT(toDocument(context)->page()); 261 NotificationController::from(toDocument(context)->page())->client()->requestPermission(context, callback); 262 } 263 #endif 264 265 } // namespace WebCore 266 267 #endif // ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 268