1/*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 * Copyright (C) 2009, 2011, 2012, 2016 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)
35
36#include "Notification.h"
37
38#include "Document.h"
39#include "Event.h"
40#include "EventNames.h"
41#include "NotificationClient.h"
42#include "NotificationController.h"
43#include "NotificationPermissionCallback.h"
44#include "WindowFocusAllowedIndicator.h"
45#include <wtf/IsoMallocInlines.h>
46
47namespace WebCore {
48
49WTF_MAKE_ISO_ALLOCATED_IMPL(Notification);
50
51Ref<Notification> Notification::create(Document& context, const String& title, const Options& options)
52{
53 auto notification = adoptRef(*new Notification(context, title, options));
54 notification->suspendIfNeeded();
55 return notification;
56}
57
58Notification::Notification(Document& document, const String& title, const Options& options)
59 : ActiveDOMObject(document)
60 , m_title(title)
61 , m_direction(options.dir)
62 , m_lang(options.lang)
63 , m_body(options.body)
64 , m_tag(options.tag)
65 , m_state(Idle)
66 , m_taskTimer(std::make_unique<Timer>([this] () { show(); }))
67{
68 if (!options.icon.isEmpty()) {
69 auto iconURL = document.completeURL(options.icon);
70 if (iconURL.isValid())
71 m_icon = iconURL;
72 }
73
74 m_taskTimer->startOneShot(0_s);
75}
76
77Notification::~Notification() = default;
78
79void Notification::show()
80{
81 // prevent double-showing
82 if (m_state != Idle)
83 return;
84
85 auto* page = downcast<Document>(*scriptExecutionContext()).page();
86 if (!page)
87 return;
88
89 auto& client = NotificationController::from(page)->client();
90
91 if (client.checkPermission(scriptExecutionContext()) != Permission::Granted) {
92 dispatchErrorEvent();
93 return;
94 }
95 if (client.show(this)) {
96 m_state = Showing;
97 setPendingActivity(*this);
98 }
99}
100
101void Notification::close()
102{
103 switch (m_state) {
104 case Idle:
105 break;
106 case Showing: {
107 auto* page = downcast<Document>(*scriptExecutionContext()).page();
108 if (page)
109 NotificationController::from(page)->client().cancel(this);
110 break;
111 }
112 case Closed:
113 break;
114 }
115}
116
117const char* Notification::activeDOMObjectName() const
118{
119 return "Notification";
120}
121
122bool Notification::canSuspendForDocumentSuspension() const
123{
124 // We can suspend if the Notification is not shown yet or after it is closed.
125 return m_state == Idle || m_state == Closed;
126}
127
128void Notification::stop()
129{
130 ActiveDOMObject::stop();
131
132 auto* page = downcast<Document>(*scriptExecutionContext()).page();
133 if (page)
134 NotificationController::from(page)->client().notificationObjectDestroyed(this);
135}
136
137void Notification::finalize()
138{
139 if (m_state == Closed)
140 return;
141 m_state = Closed;
142 unsetPendingActivity(*this);
143}
144
145void Notification::dispatchShowEvent()
146{
147 dispatchEvent(Event::create(eventNames().showEvent, Event::CanBubble::No, Event::IsCancelable::No));
148}
149
150void Notification::dispatchClickEvent()
151{
152 WindowFocusAllowedIndicator windowFocusAllowed;
153 dispatchEvent(Event::create(eventNames().clickEvent, Event::CanBubble::No, Event::IsCancelable::No));
154}
155
156void Notification::dispatchCloseEvent()
157{
158 dispatchEvent(Event::create(eventNames().closeEvent, Event::CanBubble::No, Event::IsCancelable::No));
159 finalize();
160}
161
162void Notification::dispatchErrorEvent()
163{
164 dispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
165}
166
167auto Notification::permission(Document& document) -> Permission
168{
169 return NotificationController::from(document.page())->client().checkPermission(&document);
170}
171
172void Notification::requestPermission(Document& document, RefPtr<NotificationPermissionCallback>&& callback)
173{
174 NotificationController::from(document.page())->client().requestPermission(&document, WTFMove(callback));
175}
176
177} // namespace WebCore
178
179#endif // ENABLE(NOTIFICATIONS)
180