blob: b2b81323cf5347bea914373250599cc489c30ffb [file] [log] [blame]
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "NotificationPresenterClientQt.h"
#include "Document.h"
#include "DumpRenderTreeSupportQt.h"
#include "EventNames.h"
#include "KURL.h"
#include "Page.h"
#include "QtPlatformPlugin.h"
#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
#include "UserGestureIndicator.h"
#include "qwebframe_p.h"
#include "qwebkitglobal.h"
#include "qwebpage.h"
namespace WebCore {
#if ENABLE(NOTIFICATIONS)
const double notificationTimeout = 10.0;
bool NotificationPresenterClientQt::dumpNotification = false;
NotificationPresenterClientQt* s_notificationPresenter = 0;
NotificationPresenterClientQt* NotificationPresenterClientQt::notificationPresenter()
{
if (s_notificationPresenter)
return s_notificationPresenter;
s_notificationPresenter = new NotificationPresenterClientQt();
return s_notificationPresenter;
}
#endif
NotificationWrapper::NotificationWrapper()
: m_closeTimer(this, &NotificationWrapper::close)
{
#if ENABLE(NOTIFICATIONS)
#ifndef QT_NO_SYSTEMTRAYICON
m_notificationIcon = 0;
#endif
m_presenter = 0;
#endif
}
void NotificationWrapper::close(Timer<NotificationWrapper>*)
{
#if ENABLE(NOTIFICATIONS)
NotificationPresenterClientQt::notificationPresenter()->cancel(this);
#endif
}
const QString NotificationWrapper::title() const
{
#if ENABLE(NOTIFICATIONS)
Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
if (notification)
return notification->contents().title();
#endif
return QString();
}
const QString NotificationWrapper::message() const
{
#if ENABLE(NOTIFICATIONS)
Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
if (notification)
return notification->contents().body();
#endif
return QString();
}
const QByteArray NotificationWrapper::iconData() const
{
QByteArray iconData;
#if ENABLE(NOTIFICATIONS)
Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
if (notification) {
if (notification->iconData())
iconData = QByteArray::fromRawData(notification->iconData()->data(), notification->iconData()->size());
}
#endif
return iconData;
}
const QUrl NotificationWrapper::openerPageUrl() const
{
QUrl url;
#if ENABLE(NOTIFICATIONS)
Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
if (notification) {
if (notification->scriptExecutionContext())
url = static_cast<Document*>(notification->scriptExecutionContext())->page()->mainFrame()->document()->url();
}
#endif
return url;
}
void NotificationWrapper::notificationClicked()
{
#if ENABLE(NOTIFICATIONS)
NotificationPresenterClientQt::notificationPresenter()->notificationClicked(this);
#endif
}
void NotificationWrapper::notificationClosed()
{
#if ENABLE(NOTIFICATIONS)
NotificationPresenterClientQt::notificationPresenter()->cancel(this);
#endif
}
#if ENABLE(NOTIFICATIONS)
NotificationPresenterClientQt::NotificationPresenterClientQt() : m_clientCount(0)
{
}
NotificationPresenterClientQt::~NotificationPresenterClientQt()
{
while (!m_notifications.isEmpty()) {
NotificationsQueue::Iterator iter = m_notifications.begin();
detachNotification(iter.key());
}
}
void NotificationPresenterClientQt::removeClient()
{
m_clientCount--;
if (!m_clientCount) {
s_notificationPresenter = 0;
delete this;
}
}
bool NotificationPresenterClientQt::show(Notification* notification)
{
// FIXME: workers based notifications are not supported yet.
if (notification->scriptExecutionContext()->isWorkerContext())
return false;
notification->setPendingActivity(notification);
if (!notification->replaceId().isEmpty())
removeReplacedNotificationFromQueue(notification);
if (dumpNotification)
dumpShowText(notification);
QByteArray iconData;
if (notification->iconData())
iconData = QByteArray::fromRawData(notification->iconData()->data(), notification->iconData()->size());
displayNotification(notification, iconData);
notification->releaseIconData();
return true;
}
void NotificationPresenterClientQt::displayNotification(Notification* notification, const QByteArray& bytes)
{
NotificationWrapper* wrapper = new NotificationWrapper();
m_notifications.insert(notification, wrapper);
QString title;
QString message;
// FIXME: download & display HTML notifications
if (notification->isHTML())
message = notification->url().string();
else {
title = notification->contents().title();
message = notification->contents().body();
}
if (m_platformPlugin.plugin() && m_platformPlugin.plugin()->supportsExtension(QWebKitPlatformPlugin::Notifications))
wrapper->m_presenter = m_platformPlugin.createNotificationPresenter();
if (!wrapper->m_presenter) {
#ifndef QT_NO_SYSTEMTRAYICON
if (!dumpNotification)
wrapper->m_closeTimer.startOneShot(notificationTimeout);
QPixmap pixmap;
if (bytes.length() && pixmap.loadFromData(bytes)) {
QIcon icon(pixmap);
wrapper->m_notificationIcon = new QSystemTrayIcon(icon);
} else
wrapper->m_notificationIcon = new QSystemTrayIcon();
#endif
}
sendEvent(notification, "display");
// Make sure the notification was not cancelled during handling the display event
if (m_notifications.find(notification) == m_notifications.end())
return;
if (wrapper->m_presenter) {
wrapper->connect(wrapper->m_presenter.get(), SIGNAL(notificationClosed()), wrapper, SLOT(notificationClosed()), Qt::QueuedConnection);
wrapper->connect(wrapper->m_presenter.get(), SIGNAL(notificationClicked()), wrapper, SLOT(notificationClicked()));
wrapper->m_presenter->showNotification(wrapper);
return;
}
#ifndef QT_NO_SYSTEMTRAYICON
wrapper->connect(wrapper->m_notificationIcon.get(), SIGNAL(messageClicked()), wrapper, SLOT(notificationClicked()));
wrapper->m_notificationIcon->show();
wrapper->m_notificationIcon->showMessage(notification->contents().title(), notification->contents().body());
#endif
}
void NotificationPresenterClientQt::cancel(Notification* notification)
{
if (dumpNotification && notification->scriptExecutionContext()) {
if (notification->isHTML())
printf("DESKTOP NOTIFICATION CLOSED: %s\n", QString(notification->url().string()).toUtf8().constData());
else
printf("DESKTOP NOTIFICATION CLOSED: %s\n", QString(notification->contents().title()).toUtf8().constData());
}
NotificationsQueue::Iterator iter = m_notifications.find(notification);
if (iter != m_notifications.end()) {
sendEvent(notification, eventNames().closeEvent);
detachNotification(notification);
}
}
void NotificationPresenterClientQt::cancel(NotificationWrapper* wrapper)
{
Notification* notification = notificationForWrapper(wrapper);
if (notification)
cancel(notification);
}
void NotificationPresenterClientQt::notificationClicked(NotificationWrapper* wrapper)
{
Notification* notification = notificationForWrapper(wrapper);
if (notification) {
// Make sure clicks on notifications are treated as user gestures.
UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
sendEvent(notification, eventNames().clickEvent);
}
}
void NotificationPresenterClientQt::notificationClicked(const QString& title)
{
if (!dumpNotification)
return;
NotificationsQueue::ConstIterator end = m_notifications.end();
NotificationsQueue::ConstIterator iter = m_notifications.begin();
Notification* notification = 0;
while (iter != end) {
notification = iter.key();
QString notificationTitle;
if (notification->isHTML())
notificationTitle = notification->url().string();
else
notificationTitle = notification->contents().title();
if (notificationTitle == title)
break;
iter++;
}
if (notification)
sendEvent(notification, eventNames().clickEvent);
}
Notification* NotificationPresenterClientQt::notificationForWrapper(const NotificationWrapper* wrapper) const
{
NotificationsQueue::ConstIterator end = m_notifications.end();
NotificationsQueue::ConstIterator iter = m_notifications.begin();
while (iter != end && iter.value() != wrapper)
iter++;
if (iter != end)
return iter.key();
return 0;
}
void NotificationPresenterClientQt::notificationObjectDestroyed(Notification* notification)
{
// Called from ~Notification(), Remove the entry from the notifications list and delete the icon.
NotificationsQueue::Iterator iter = m_notifications.find(notification);
if (iter != m_notifications.end())
delete m_notifications.take(notification);
}
void NotificationPresenterClientQt::requestPermission(ScriptExecutionContext* context, PassRefPtr<VoidCallback> callback)
{
if (dumpNotification)
printf("DESKTOP NOTIFICATION PERMISSION REQUESTED: %s\n", QString(context->securityOrigin()->toString()).toUtf8().constData());
QHash<ScriptExecutionContext*, CallbacksInfo >::iterator iter = m_pendingPermissionRequests.find(context);
if (iter != m_pendingPermissionRequests.end())
iter.value().m_callbacks.append(callback);
else {
RefPtr<VoidCallback> cb = callback;
CallbacksInfo info;
info.m_frame = toFrame(context);
info.m_callbacks.append(cb);
m_pendingPermissionRequests.insert(context, info);
if (toPage(context) && toFrame(context)) {
m_pendingPermissionRequests.insert(context, info);
emit toPage(context)->featurePermissionRequested(toFrame(context), QWebPage::Notifications);
}
}
}
NotificationPresenter::Permission NotificationPresenterClientQt::checkPermission(ScriptExecutionContext* context)
{
return m_cachedPermissions.value(context, NotificationPresenter::PermissionNotAllowed);
}
void NotificationPresenterClientQt::cancelRequestsForPermission(ScriptExecutionContext* context)
{
m_cachedPermissions.remove(context);
QHash<ScriptExecutionContext*, CallbacksInfo >::iterator iter = m_pendingPermissionRequests.find(context);
if (iter == m_pendingPermissionRequests.end())
return;
QWebFrame* frame = iter.value().m_frame;
if (!frame)
return;
QWebPage* page = frame->page();
m_pendingPermissionRequests.erase(iter);
if (!page)
return;
if (dumpNotification)
printf("DESKTOP NOTIFICATION PERMISSION REQUEST CANCELLED: %s\n", QString(context->securityOrigin()->toString()).toUtf8().constData());
emit page->featurePermissionRequestCanceled(frame, QWebPage::Notifications);
}
void NotificationPresenterClientQt::allowNotificationForFrame(Frame* frame)
{
m_cachedPermissions.insert(frame->document(), NotificationPresenter::PermissionAllowed);
QHash<ScriptExecutionContext*, CallbacksInfo>::iterator iter = m_pendingPermissionRequests.begin();
while (iter != m_pendingPermissionRequests.end()) {
if (iter.key() == frame->document())
break;
}
if (iter == m_pendingPermissionRequests.end())
return;
QList<RefPtr<VoidCallback> >& callbacks = iter.value().m_callbacks;
for (int i = 0; i < callbacks.size(); i++)
callbacks.at(i)->handleEvent();
m_pendingPermissionRequests.remove(iter.key());
}
void NotificationPresenterClientQt::sendEvent(Notification* notification, const AtomicString& eventName)
{
if (notification->scriptExecutionContext())
notification->dispatchEvent(Event::create(eventName, false, true));
}
void NotificationPresenterClientQt::removeReplacedNotificationFromQueue(Notification* notification)
{
Notification* oldNotification = 0;
NotificationsQueue::Iterator end = m_notifications.end();
NotificationsQueue::Iterator iter = m_notifications.begin();
while (iter != end) {
Notification* existingNotification = iter.key();
if (existingNotification->replaceId() == notification->replaceId() && existingNotification->url().protocol() == notification->url().protocol() && existingNotification->url().host() == notification->url().host()) {
oldNotification = iter.key();
break;
}
iter++;
}
if (oldNotification) {
if (dumpNotification)
dumpReplacedIdText(oldNotification);
sendEvent(oldNotification, eventNames().closeEvent);
detachNotification(oldNotification);
}
}
void NotificationPresenterClientQt::detachNotification(Notification* notification)
{
delete m_notifications.take(notification);
notification->detachPresenter();
notification->unsetPendingActivity(notification);
}
void NotificationPresenterClientQt::dumpReplacedIdText(Notification* notification)
{
if (notification)
printf("REPLACING NOTIFICATION %s\n", notification->isHTML() ? QString(notification->url().string()).toUtf8().constData() : QString(notification->contents().title()).toUtf8().constData());
}
void NotificationPresenterClientQt::dumpShowText(Notification* notification)
{
if (notification->isHTML())
printf("DESKTOP NOTIFICATION: contents at %s\n", QString(notification->url().string()).toUtf8().constData());
else {
printf("DESKTOP NOTIFICATION:%s icon %s, title %s, text %s\n",
notification->dir() == "rtl" ? "(RTL)" : "",
QString(notification->contents().icon().string()).toUtf8().constData(), QString(notification->contents().title()).toUtf8().constData(),
QString(notification->contents().body()).toUtf8().constData());
}
}
QWebPage* NotificationPresenterClientQt::toPage(ScriptExecutionContext* context)
{
if (!context || context->isWorkerContext())
return 0;
Document* document = static_cast<Document*>(context);
Page* page = document->page();
if (!page || !page->mainFrame())
return 0;
return QWebFramePrivate::kit(page->mainFrame())->page();
}
QWebFrame* NotificationPresenterClientQt::toFrame(ScriptExecutionContext* context)
{
if (!context || context->isWorkerContext())
return 0;
Document* document = static_cast<Document*>(context);
if (!document || !document->frame())
return 0;
return QWebFramePrivate::kit(document->frame());
}
#endif // ENABLE(NOTIFICATIONS)
}
#include "moc_NotificationPresenterClientQt.cpp"