Home | History | Annotate | Download | only in notifications
      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