tizen beta release
[profile/ivi/webkit-efl.git] / Source / WebKit / qt / WebCoreSupport / NotificationPresenterClientQt.cpp
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
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 #include "NotificationPresenterClientQt.h"
34
35 #include "Document.h"
36 #include "DumpRenderTreeSupportQt.h"
37 #include "EventNames.h"
38 #include "KURL.h"
39 #include "Page.h"
40 #include "QtPlatformPlugin.h"
41 #include "ScriptExecutionContext.h"
42 #include "SecurityOrigin.h"
43 #include "UserGestureIndicator.h"
44
45 #include "qwebframe_p.h"
46 #include "qwebkitglobal.h"
47 #include "qwebpage.h"
48
49 namespace WebCore {
50
51 #if ENABLE(NOTIFICATIONS)
52
53 const double notificationTimeout = 10.0;
54
55 bool NotificationPresenterClientQt::dumpNotification = false;
56
57 NotificationPresenterClientQt* s_notificationPresenter = 0;
58
59 NotificationPresenterClientQt* NotificationPresenterClientQt::notificationPresenter()
60 {
61     if (s_notificationPresenter)
62         return s_notificationPresenter;
63
64     s_notificationPresenter = new NotificationPresenterClientQt();
65     return s_notificationPresenter;
66 }
67
68 #endif
69
70 NotificationWrapper::NotificationWrapper()
71     : m_closeTimer(this, &NotificationWrapper::close)
72 {
73 #if ENABLE(NOTIFICATIONS)
74
75 #ifndef QT_NO_SYSTEMTRAYICON
76     m_notificationIcon = nullptr;
77 #endif
78     m_presenter = nullptr;
79 #endif
80 }
81
82 void NotificationWrapper::close(Timer<NotificationWrapper>*)
83 {
84 #if ENABLE(NOTIFICATIONS)
85     NotificationPresenterClientQt::notificationPresenter()->cancel(this);
86 #endif
87 }
88
89 const QString NotificationWrapper::title() const
90 {
91 #if ENABLE(NOTIFICATIONS)
92     Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
93     if (notification)
94         return notification->contents().title;
95 #endif
96     return QString();
97 }
98
99 const QString NotificationWrapper::message() const
100 {
101 #if ENABLE(NOTIFICATIONS)
102     Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
103     if (notification)
104         return notification->contents().body;
105 #endif
106     return QString();
107 }
108
109 const QByteArray NotificationWrapper::iconData() const
110 {
111     QByteArray iconData;
112 #if ENABLE(NOTIFICATIONS)
113     Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
114     if (notification) {
115         if (notification->iconData())
116             iconData = QByteArray::fromRawData(notification->iconData()->data(), notification->iconData()->size());
117     }
118 #endif
119     return iconData;
120 }
121
122 const QUrl NotificationWrapper::openerPageUrl() const
123 {
124     QUrl url;
125 #if ENABLE(NOTIFICATIONS)
126     Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
127     if (notification) {
128         if (notification->scriptExecutionContext()) 
129             url = static_cast<Document*>(notification->scriptExecutionContext())->page()->mainFrame()->document()->url();
130     }
131 #endif
132     return url;
133 }
134
135 void NotificationWrapper::notificationClicked()
136 {
137 #if ENABLE(NOTIFICATIONS)
138     NotificationPresenterClientQt::notificationPresenter()->notificationClicked(this);
139 #endif
140 }
141
142 void NotificationWrapper::notificationClosed()
143 {
144 #if ENABLE(NOTIFICATIONS)
145     NotificationPresenterClientQt::notificationPresenter()->cancel(this);
146 #endif
147 }
148
149 #if ENABLE(NOTIFICATIONS)
150
151 NotificationPresenterClientQt::NotificationPresenterClientQt() : m_clientCount(0)
152 {
153 }
154
155 NotificationPresenterClientQt::~NotificationPresenterClientQt()
156 {
157     while (!m_notifications.isEmpty()) {
158         NotificationsQueue::Iterator iter = m_notifications.begin();
159         detachNotification(iter.key());
160     }
161 }
162
163 void NotificationPresenterClientQt::removeClient()
164 {
165     m_clientCount--;
166     if (!m_clientCount) {
167         s_notificationPresenter = 0;
168         delete this;
169     }
170 }
171
172 bool NotificationPresenterClientQt::show(Notification* notification)
173 {
174     // FIXME: workers based notifications are not supported yet.
175     if (notification->scriptExecutionContext()->isWorkerContext())
176         return false;
177     notification->setPendingActivity(notification);
178     if (!notification->replaceId().isEmpty())
179         removeReplacedNotificationFromQueue(notification);
180     if (dumpNotification)
181         dumpShowText(notification);
182     QByteArray iconData;
183     if (notification->iconData())
184         iconData = QByteArray::fromRawData(notification->iconData()->data(), notification->iconData()->size());
185     displayNotification(notification, iconData);
186     notification->releaseIconData();
187     return true;
188 }
189
190 void NotificationPresenterClientQt::displayNotification(Notification* notification, const QByteArray& bytes)
191 {
192     NotificationWrapper* wrapper = new NotificationWrapper();
193     m_notifications.insert(notification, wrapper);
194     QString title;
195     QString message;
196     // FIXME: download & display HTML notifications
197     if (notification->isHTML())
198         message = notification->url().string();
199     else {
200         title = notification->contents().title;
201         message = notification->contents().body;
202     }
203
204     if (m_platformPlugin.plugin() && m_platformPlugin.plugin()->supportsExtension(QWebKitPlatformPlugin::Notifications))
205         wrapper->m_presenter = m_platformPlugin.createNotificationPresenter();
206
207     if (!wrapper->m_presenter) {
208 #ifndef QT_NO_SYSTEMTRAYICON
209         if (!dumpNotification)
210             wrapper->m_closeTimer.startOneShot(notificationTimeout);
211         QPixmap pixmap;
212         if (bytes.length() && pixmap.loadFromData(bytes)) {
213             QIcon icon(pixmap);
214             wrapper->m_notificationIcon = adoptPtr(new QSystemTrayIcon(icon));
215         } else
216             wrapper->m_notificationIcon = adoptPtr(new QSystemTrayIcon());
217 #endif
218     }
219
220     sendEvent(notification, "display");
221
222     // Make sure the notification was not cancelled during handling the display event
223     if (m_notifications.find(notification) == m_notifications.end())
224         return;
225
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);
230         return;
231     }
232
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);
237 #endif
238 }
239
240 void NotificationPresenterClientQt::cancel(Notification* notification)
241 {
242     if (dumpNotification && notification->scriptExecutionContext()) {
243         if (notification->isHTML())
244             printf("DESKTOP NOTIFICATION CLOSED: %s\n", QString(notification->url().string()).toUtf8().constData());
245         else
246             printf("DESKTOP NOTIFICATION CLOSED: %s\n", QString(notification->contents().title).toUtf8().constData());
247     }
248
249     NotificationsQueue::Iterator iter = m_notifications.find(notification);
250     if (iter != m_notifications.end()) {
251         sendEvent(notification, eventNames().closeEvent);
252         detachNotification(notification);
253     }
254 }
255
256 void NotificationPresenterClientQt::cancel(NotificationWrapper* wrapper)
257 {
258     Notification* notification = notificationForWrapper(wrapper);
259     if (notification)
260         cancel(notification);
261 }
262
263 void NotificationPresenterClientQt::notificationClicked(NotificationWrapper* wrapper)
264 {
265     Notification* notification =  notificationForWrapper(wrapper);
266     if (notification) {
267         // Make sure clicks on notifications are treated as user gestures.
268         UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
269         sendEvent(notification, eventNames().clickEvent);
270     }
271 }
272
273 void NotificationPresenterClientQt::notificationClicked(const QString& title)
274 {
275     if (!dumpNotification)
276         return;
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();
285         else
286             notificationTitle = notification->contents().title;
287         if (notificationTitle == title)
288             break;
289         iter++;
290     }
291     if (notification)
292         sendEvent(notification, eventNames().clickEvent);
293 }
294
295 Notification* NotificationPresenterClientQt::notificationForWrapper(const NotificationWrapper* wrapper) const
296 {
297     NotificationsQueue::ConstIterator end = m_notifications.end();
298     NotificationsQueue::ConstIterator iter = m_notifications.begin();
299     while (iter != end && iter.value() != wrapper)
300         iter++;
301     if (iter != end)
302         return iter.key();
303     return 0;
304 }
305
306 void NotificationPresenterClientQt::notificationObjectDestroyed(Notification* notification)
307 {
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);
312 }
313
314 void NotificationPresenterClientQt::requestPermission(ScriptExecutionContext* context, PassRefPtr<VoidCallback> callback)
315 {  
316     if (dumpNotification)
317         printf("DESKTOP NOTIFICATION PERMISSION REQUESTED: %s\n", QString(context->securityOrigin()->toString()).toUtf8().constData());
318
319     QHash<ScriptExecutionContext*, CallbacksInfo >::iterator iter = m_pendingPermissionRequests.find(context);
320     if (iter != m_pendingPermissionRequests.end())
321         iter.value().m_callbacks.append(callback);
322     else {
323         RefPtr<VoidCallback> cb = callback;
324         CallbacksInfo info;
325         info.m_frame = toFrame(context);
326         info.m_callbacks.append(cb);
327         m_pendingPermissionRequests.insert(context, info);
328
329         if (toPage(context) && toFrame(context)) {
330             m_pendingPermissionRequests.insert(context, info);
331             emit toPage(context)->featurePermissionRequested(toFrame(context), QWebPage::Notifications);
332         }
333     }
334 }
335
336 NotificationPresenter::Permission NotificationPresenterClientQt::checkPermission(ScriptExecutionContext* context)
337 {
338     return m_cachedPermissions.value(context, NotificationPresenter::PermissionNotAllowed);
339 }
340
341 void NotificationPresenterClientQt::cancelRequestsForPermission(ScriptExecutionContext* context)
342 {
343     m_cachedPermissions.remove(context);
344
345     QHash<ScriptExecutionContext*, CallbacksInfo >::iterator iter = m_pendingPermissionRequests.find(context);
346     if (iter == m_pendingPermissionRequests.end())
347         return;
348
349     QWebFrame* frame = iter.value().m_frame;
350     if (!frame)
351         return;
352     QWebPage* page = frame->page();
353     m_pendingPermissionRequests.erase(iter);
354
355     if (!page)
356         return;
357
358     if (dumpNotification)
359         printf("DESKTOP NOTIFICATION PERMISSION REQUEST CANCELLED: %s\n", QString(context->securityOrigin()->toString()).toUtf8().constData());
360
361     emit page->featurePermissionRequestCanceled(frame, QWebPage::Notifications);
362 }
363
364 void NotificationPresenterClientQt::allowNotificationForFrame(Frame* frame)
365 {
366     m_cachedPermissions.insert(frame->document(), NotificationPresenter::PermissionAllowed);
367
368     QHash<ScriptExecutionContext*,  CallbacksInfo>::iterator iter = m_pendingPermissionRequests.begin();
369     while (iter != m_pendingPermissionRequests.end()) {
370         if (iter.key() == frame->document())
371             break;
372     }
373
374     if (iter == m_pendingPermissionRequests.end())
375         return;
376
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());
381 }
382
383 void NotificationPresenterClientQt::sendEvent(Notification* notification, const AtomicString& eventName)
384 {
385     if (notification->scriptExecutionContext())
386         notification->dispatchEvent(Event::create(eventName, false, true));
387 }
388
389 void NotificationPresenterClientQt::removeReplacedNotificationFromQueue(Notification* notification)
390 {
391     Notification* oldNotification = 0;
392     NotificationsQueue::Iterator end = m_notifications.end();
393     NotificationsQueue::Iterator iter = m_notifications.begin();
394
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();
399             break;
400         }
401         iter++;
402     }
403
404     if (oldNotification) {
405         if (dumpNotification)
406             dumpReplacedIdText(oldNotification);
407         sendEvent(oldNotification, eventNames().closeEvent);
408         detachNotification(oldNotification);
409     }
410 }
411
412 void NotificationPresenterClientQt::detachNotification(Notification* notification)
413 {
414     delete m_notifications.take(notification);
415     notification->detachPresenter();
416     notification->unsetPendingActivity(notification);
417 }
418
419 void NotificationPresenterClientQt::dumpReplacedIdText(Notification* notification)
420 {
421     if (notification)
422         printf("REPLACING NOTIFICATION %s\n", notification->isHTML() ? QString(notification->url().string()).toUtf8().constData() : QString(notification->contents().title).toUtf8().constData());
423 }
424
425 void NotificationPresenterClientQt::dumpShowText(Notification* notification)
426 {
427     if (notification->isHTML())
428         printf("DESKTOP NOTIFICATION: contents at %s\n", QString(notification->url().string()).toUtf8().constData());
429     else {
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());
434     }
435 }
436
437 QWebPage* NotificationPresenterClientQt::toPage(ScriptExecutionContext* context)
438 {
439     if (!context || context->isWorkerContext())
440         return 0;
441
442     Document* document = static_cast<Document*>(context);
443
444     Page* page = document->page();
445     if (!page || !page->mainFrame())
446         return 0;
447
448     return QWebFramePrivate::kit(page->mainFrame())->page();
449 }
450
451 QWebFrame* NotificationPresenterClientQt::toFrame(ScriptExecutionContext* context)
452 {
453     if (!context || context->isWorkerContext())
454         return 0;
455
456     Document* document = static_cast<Document*>(context);
457     if (!document || !document->frame())
458         return 0;
459
460     return QWebFramePrivate::kit(document->frame());
461 }
462
463 #endif // ENABLE(NOTIFICATIONS)
464 }
465
466 #include "moc_NotificationPresenterClientQt.cpp"