summaryrefslogtreecommitdiffstats
path: root/Source/WebKit/qt/WebCoreSupport/NotificationPresenterClientQt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/qt/WebCoreSupport/NotificationPresenterClientQt.cpp')
-rw-r--r--Source/WebKit/qt/WebCoreSupport/NotificationPresenterClientQt.cpp468
1 files changed, 468 insertions, 0 deletions
diff --git a/Source/WebKit/qt/WebCoreSupport/NotificationPresenterClientQt.cpp b/Source/WebKit/qt/WebCoreSupport/NotificationPresenterClientQt.cpp
new file mode 100644
index 0000000..994cd85
--- /dev/null
+++ b/Source/WebKit/qt/WebCoreSupport/NotificationPresenterClientQt.cpp
@@ -0,0 +1,468 @@
+/*
+ * 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);
+ if (!document)
+ return 0;
+
+ 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"