2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "NotificationPresenterClientQt.h"
36 #include "DumpRenderTreeSupportQt.h"
37 #include "EventNames.h"
40 #include "QtPlatformPlugin.h"
41 #include "ScriptExecutionContext.h"
42 #include "SecurityOrigin.h"
43 #include "UserGestureIndicator.h"
45 #include "qwebframe_p.h"
46 #include "qwebkitglobal.h"
51 #if ENABLE(NOTIFICATIONS)
53 const double notificationTimeout = 10.0;
55 bool NotificationPresenterClientQt::dumpNotification = false;
57 NotificationPresenterClientQt* s_notificationPresenter = 0;
59 NotificationPresenterClientQt* NotificationPresenterClientQt::notificationPresenter()
61 if (s_notificationPresenter)
62 return s_notificationPresenter;
64 s_notificationPresenter = new NotificationPresenterClientQt();
65 return s_notificationPresenter;
70 NotificationWrapper::NotificationWrapper()
71 : m_closeTimer(this, &NotificationWrapper::close)
73 #if ENABLE(NOTIFICATIONS)
75 #ifndef QT_NO_SYSTEMTRAYICON
76 m_notificationIcon = nullptr;
78 m_presenter = nullptr;
82 void NotificationWrapper::close(Timer<NotificationWrapper>*)
84 #if ENABLE(NOTIFICATIONS)
85 NotificationPresenterClientQt::notificationPresenter()->cancel(this);
89 const QString NotificationWrapper::title() const
91 #if ENABLE(NOTIFICATIONS)
92 Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
94 return notification->contents().title;
99 const QString NotificationWrapper::message() const
101 #if ENABLE(NOTIFICATIONS)
102 Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
104 return notification->contents().body;
109 const QByteArray NotificationWrapper::iconData() const
112 #if ENABLE(NOTIFICATIONS)
113 Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
115 if (notification->iconData())
116 iconData = QByteArray::fromRawData(notification->iconData()->data(), notification->iconData()->size());
122 const QUrl NotificationWrapper::openerPageUrl() const
125 #if ENABLE(NOTIFICATIONS)
126 Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
128 if (notification->scriptExecutionContext())
129 url = static_cast<Document*>(notification->scriptExecutionContext())->page()->mainFrame()->document()->url();
135 void NotificationWrapper::notificationClicked()
137 #if ENABLE(NOTIFICATIONS)
138 NotificationPresenterClientQt::notificationPresenter()->notificationClicked(this);
142 void NotificationWrapper::notificationClosed()
144 #if ENABLE(NOTIFICATIONS)
145 NotificationPresenterClientQt::notificationPresenter()->cancel(this);
149 #if ENABLE(NOTIFICATIONS)
151 NotificationPresenterClientQt::NotificationPresenterClientQt() : m_clientCount(0)
155 NotificationPresenterClientQt::~NotificationPresenterClientQt()
157 while (!m_notifications.isEmpty()) {
158 NotificationsQueue::Iterator iter = m_notifications.begin();
159 detachNotification(iter.key());
163 void NotificationPresenterClientQt::removeClient()
166 if (!m_clientCount) {
167 s_notificationPresenter = 0;
172 bool NotificationPresenterClientQt::show(Notification* notification)
174 // FIXME: workers based notifications are not supported yet.
175 if (notification->scriptExecutionContext()->isWorkerContext())
177 notification->setPendingActivity(notification);
178 if (!notification->replaceId().isEmpty())
179 removeReplacedNotificationFromQueue(notification);
180 if (dumpNotification)
181 dumpShowText(notification);
183 if (notification->iconData())
184 iconData = QByteArray::fromRawData(notification->iconData()->data(), notification->iconData()->size());
185 displayNotification(notification, iconData);
186 notification->releaseIconData();
190 void NotificationPresenterClientQt::displayNotification(Notification* notification, const QByteArray& bytes)
192 NotificationWrapper* wrapper = new NotificationWrapper();
193 m_notifications.insert(notification, wrapper);
196 // FIXME: download & display HTML notifications
197 if (notification->isHTML())
198 message = notification->url().string();
200 title = notification->contents().title;
201 message = notification->contents().body;
204 if (m_platformPlugin.plugin() && m_platformPlugin.plugin()->supportsExtension(QWebKitPlatformPlugin::Notifications))
205 wrapper->m_presenter = m_platformPlugin.createNotificationPresenter();
207 if (!wrapper->m_presenter) {
208 #ifndef QT_NO_SYSTEMTRAYICON
209 if (!dumpNotification)
210 wrapper->m_closeTimer.startOneShot(notificationTimeout);
212 if (bytes.length() && pixmap.loadFromData(bytes)) {
214 wrapper->m_notificationIcon = adoptPtr(new QSystemTrayIcon(icon));
216 wrapper->m_notificationIcon = adoptPtr(new QSystemTrayIcon());
220 sendEvent(notification, "display");
222 // Make sure the notification was not cancelled during handling the display event
223 if (m_notifications.find(notification) == m_notifications.end())
226 if (wrapper->m_presenter) {
227 wrapper->connect(wrapper->m_presenter.get(), SIGNAL(notificationClosed()), wrapper, SLOT(notificationClosed()), Qt::QueuedConnection);
228 wrapper->connect(wrapper->m_presenter.get(), SIGNAL(notificationClicked()), wrapper, SLOT(notificationClicked()));
229 wrapper->m_presenter->showNotification(wrapper);
233 #ifndef QT_NO_SYSTEMTRAYICON
234 wrapper->connect(wrapper->m_notificationIcon.get(), SIGNAL(messageClicked()), wrapper, SLOT(notificationClicked()));
235 wrapper->m_notificationIcon->show();
236 wrapper->m_notificationIcon->showMessage(notification->contents().title, notification->contents().body);
240 void NotificationPresenterClientQt::cancel(Notification* notification)
242 if (dumpNotification && notification->scriptExecutionContext()) {
243 if (notification->isHTML())
244 printf("DESKTOP NOTIFICATION CLOSED: %s\n", QString(notification->url().string()).toUtf8().constData());
246 printf("DESKTOP NOTIFICATION CLOSED: %s\n", QString(notification->contents().title).toUtf8().constData());
249 NotificationsQueue::Iterator iter = m_notifications.find(notification);
250 if (iter != m_notifications.end()) {
251 sendEvent(notification, eventNames().closeEvent);
252 detachNotification(notification);
256 void NotificationPresenterClientQt::cancel(NotificationWrapper* wrapper)
258 Notification* notification = notificationForWrapper(wrapper);
260 cancel(notification);
263 void NotificationPresenterClientQt::notificationClicked(NotificationWrapper* wrapper)
265 Notification* notification = notificationForWrapper(wrapper);
267 // Make sure clicks on notifications are treated as user gestures.
268 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
269 sendEvent(notification, eventNames().clickEvent);
273 void NotificationPresenterClientQt::notificationClicked(const QString& title)
275 if (!dumpNotification)
277 NotificationsQueue::ConstIterator end = m_notifications.end();
278 NotificationsQueue::ConstIterator iter = m_notifications.begin();
279 Notification* notification = 0;
280 while (iter != end) {
281 notification = iter.key();
282 QString notificationTitle;
283 if (notification->isHTML())
284 notificationTitle = notification->url().string();
286 notificationTitle = notification->contents().title;
287 if (notificationTitle == title)
292 sendEvent(notification, eventNames().clickEvent);
295 Notification* NotificationPresenterClientQt::notificationForWrapper(const NotificationWrapper* wrapper) const
297 NotificationsQueue::ConstIterator end = m_notifications.end();
298 NotificationsQueue::ConstIterator iter = m_notifications.begin();
299 while (iter != end && iter.value() != wrapper)
306 void NotificationPresenterClientQt::notificationObjectDestroyed(Notification* notification)
308 // Called from ~Notification(), Remove the entry from the notifications list and delete the icon.
309 NotificationsQueue::Iterator iter = m_notifications.find(notification);
310 if (iter != m_notifications.end())
311 delete m_notifications.take(notification);
314 void NotificationPresenterClientQt::requestPermission(ScriptExecutionContext* context, PassRefPtr<VoidCallback> callback)
316 if (dumpNotification)
317 printf("DESKTOP NOTIFICATION PERMISSION REQUESTED: %s\n", QString(context->securityOrigin()->toString()).toUtf8().constData());
319 QHash<ScriptExecutionContext*, CallbacksInfo >::iterator iter = m_pendingPermissionRequests.find(context);
320 if (iter != m_pendingPermissionRequests.end())
321 iter.value().m_callbacks.append(callback);
323 RefPtr<VoidCallback> cb = callback;
325 info.m_frame = toFrame(context);
326 info.m_callbacks.append(cb);
327 m_pendingPermissionRequests.insert(context, info);
329 if (toPage(context) && toFrame(context)) {
330 m_pendingPermissionRequests.insert(context, info);
331 emit toPage(context)->featurePermissionRequested(toFrame(context), QWebPage::Notifications);
336 NotificationPresenter::Permission NotificationPresenterClientQt::checkPermission(ScriptExecutionContext* context)
338 return m_cachedPermissions.value(context, NotificationPresenter::PermissionNotAllowed);
341 void NotificationPresenterClientQt::cancelRequestsForPermission(ScriptExecutionContext* context)
343 m_cachedPermissions.remove(context);
345 QHash<ScriptExecutionContext*, CallbacksInfo >::iterator iter = m_pendingPermissionRequests.find(context);
346 if (iter == m_pendingPermissionRequests.end())
349 QWebFrame* frame = iter.value().m_frame;
352 QWebPage* page = frame->page();
353 m_pendingPermissionRequests.erase(iter);
358 if (dumpNotification)
359 printf("DESKTOP NOTIFICATION PERMISSION REQUEST CANCELLED: %s\n", QString(context->securityOrigin()->toString()).toUtf8().constData());
361 emit page->featurePermissionRequestCanceled(frame, QWebPage::Notifications);
364 void NotificationPresenterClientQt::allowNotificationForFrame(Frame* frame)
366 m_cachedPermissions.insert(frame->document(), NotificationPresenter::PermissionAllowed);
368 QHash<ScriptExecutionContext*, CallbacksInfo>::iterator iter = m_pendingPermissionRequests.begin();
369 while (iter != m_pendingPermissionRequests.end()) {
370 if (iter.key() == frame->document())
374 if (iter == m_pendingPermissionRequests.end())
377 QList<RefPtr<VoidCallback> >& callbacks = iter.value().m_callbacks;
378 for (int i = 0; i < callbacks.size(); i++)
379 callbacks.at(i)->handleEvent();
380 m_pendingPermissionRequests.remove(iter.key());
383 void NotificationPresenterClientQt::sendEvent(Notification* notification, const AtomicString& eventName)
385 if (notification->scriptExecutionContext())
386 notification->dispatchEvent(Event::create(eventName, false, true));
389 void NotificationPresenterClientQt::removeReplacedNotificationFromQueue(Notification* notification)
391 Notification* oldNotification = 0;
392 NotificationsQueue::Iterator end = m_notifications.end();
393 NotificationsQueue::Iterator iter = m_notifications.begin();
395 while (iter != end) {
396 Notification* existingNotification = iter.key();
397 if (existingNotification->replaceId() == notification->replaceId() && existingNotification->url().protocol() == notification->url().protocol() && existingNotification->url().host() == notification->url().host()) {
398 oldNotification = iter.key();
404 if (oldNotification) {
405 if (dumpNotification)
406 dumpReplacedIdText(oldNotification);
407 sendEvent(oldNotification, eventNames().closeEvent);
408 detachNotification(oldNotification);
412 void NotificationPresenterClientQt::detachNotification(Notification* notification)
414 delete m_notifications.take(notification);
415 notification->detachPresenter();
416 notification->unsetPendingActivity(notification);
419 void NotificationPresenterClientQt::dumpReplacedIdText(Notification* notification)
422 printf("REPLACING NOTIFICATION %s\n", notification->isHTML() ? QString(notification->url().string()).toUtf8().constData() : QString(notification->contents().title).toUtf8().constData());
425 void NotificationPresenterClientQt::dumpShowText(Notification* notification)
427 if (notification->isHTML())
428 printf("DESKTOP NOTIFICATION: contents at %s\n", QString(notification->url().string()).toUtf8().constData());
430 printf("DESKTOP NOTIFICATION:%s icon %s, title %s, text %s\n",
431 notification->dir() == "rtl" ? "(RTL)" : "",
432 QString(notification->contents().icon.string()).toUtf8().constData(), QString(notification->contents().title).toUtf8().constData(),
433 QString(notification->contents().body).toUtf8().constData());
437 QWebPage* NotificationPresenterClientQt::toPage(ScriptExecutionContext* context)
439 if (!context || context->isWorkerContext())
442 Document* document = static_cast<Document*>(context);
444 Page* page = document->page();
445 if (!page || !page->mainFrame())
448 return QWebFramePrivate::kit(page->mainFrame())->page();
451 QWebFrame* NotificationPresenterClientQt::toFrame(ScriptExecutionContext* context)
453 if (!context || context->isWorkerContext())
456 Document* document = static_cast<Document*>(context);
457 if (!document || !document->frame())
460 return QWebFramePrivate::kit(document->frame());
463 #endif // ENABLE(NOTIFICATIONS)
466 #include "moc_NotificationPresenterClientQt.cpp"