NotificationPresenterClientQt.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  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. #include "config.h"
  32. #include "NotificationPresenterClientQt.h"
  33. #include "Document.h"
  34. #include "Event.h"
  35. #include "EventNames.h"
  36. #include "KURL.h"
  37. #include "Page.h"
  38. #include "QWebFrameAdapter.h"
  39. #include "QWebPageAdapter.h"
  40. #include "QtPlatformPlugin.h"
  41. #include "ScriptExecutionContext.h"
  42. #include "SecurityOrigin.h"
  43. #include "UserGestureIndicator.h"
  44. #include "qwebkitglobal.h"
  45. namespace WebCore {
  46. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  47. const double notificationTimeout = 10.0;
  48. bool NotificationPresenterClientQt::dumpNotification = false;
  49. NotificationPresenterClientQt* s_notificationPresenter = 0;
  50. NotificationPresenterClientQt* NotificationPresenterClientQt::notificationPresenter()
  51. {
  52. if (s_notificationPresenter)
  53. return s_notificationPresenter;
  54. s_notificationPresenter = new NotificationPresenterClientQt();
  55. return s_notificationPresenter;
  56. }
  57. #endif
  58. NotificationWrapper::NotificationWrapper()
  59. : m_closeTimer(this, &NotificationWrapper::close)
  60. , m_displayEventTimer(this, &NotificationWrapper::sendDisplayEvent)
  61. {
  62. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  63. m_presenter = nullptr;
  64. #endif
  65. }
  66. void NotificationWrapper::close(Timer<NotificationWrapper>*)
  67. {
  68. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  69. NotificationPresenterClientQt::notificationPresenter()->cancel(this);
  70. #endif
  71. }
  72. void NotificationWrapper::sendDisplayEvent(Timer<NotificationWrapper>*)
  73. {
  74. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  75. NotificationPresenterClientQt::notificationPresenter()->sendDisplayEvent(this);
  76. #endif
  77. }
  78. const QString NotificationWrapper::title() const
  79. {
  80. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  81. Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
  82. if (notification)
  83. return notification->title();
  84. #endif
  85. return QString();
  86. }
  87. const QString NotificationWrapper::message() const
  88. {
  89. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  90. Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
  91. if (notification)
  92. return notification->body();
  93. #endif
  94. return QString();
  95. }
  96. const QUrl NotificationWrapper::iconUrl() const
  97. {
  98. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  99. Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
  100. if (notification)
  101. return notification->iconURL();
  102. #endif
  103. return QUrl();
  104. }
  105. const QUrl NotificationWrapper::openerPageUrl() const
  106. {
  107. QUrl url;
  108. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  109. Notification* notification = NotificationPresenterClientQt::notificationPresenter()->notificationForWrapper(this);
  110. if (notification) {
  111. if (notification->scriptExecutionContext())
  112. url = static_cast<Document*>(notification->scriptExecutionContext())->page()->mainFrame()->document()->url();
  113. }
  114. #endif
  115. return url;
  116. }
  117. void NotificationWrapper::notificationClicked()
  118. {
  119. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  120. NotificationPresenterClientQt::notificationPresenter()->notificationClicked(this);
  121. #endif
  122. }
  123. void NotificationWrapper::notificationClosed()
  124. {
  125. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  126. NotificationPresenterClientQt::notificationPresenter()->cancel(this);
  127. #endif
  128. }
  129. #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  130. NotificationPresenterClientQt::NotificationPresenterClientQt() : m_clientCount(0)
  131. {
  132. }
  133. NotificationPresenterClientQt::~NotificationPresenterClientQt()
  134. {
  135. while (!m_notifications.isEmpty()) {
  136. NotificationsQueue::Iterator iter = m_notifications.begin();
  137. detachNotification(iter.key());
  138. }
  139. }
  140. void NotificationPresenterClientQt::removeClient()
  141. {
  142. m_clientCount--;
  143. if (!m_clientCount) {
  144. s_notificationPresenter = 0;
  145. delete this;
  146. }
  147. }
  148. bool NotificationPresenterClientQt::show(Notification* notification)
  149. {
  150. // FIXME: workers based notifications are not supported yet.
  151. if (notification->scriptExecutionContext()->isWorkerContext())
  152. return false;
  153. notification->setPendingActivity(notification);
  154. if (!notification->tag().isEmpty())
  155. removeReplacedNotificationFromQueue(notification);
  156. if (dumpNotification)
  157. dumpShowText(notification);
  158. displayNotification(notification);
  159. return true;
  160. }
  161. void NotificationPresenterClientQt::displayNotification(Notification* notification)
  162. {
  163. NotificationWrapper* wrapper = new NotificationWrapper();
  164. m_notifications.insert(notification, wrapper);
  165. QString title = notification->title();
  166. QString message = notification->body();
  167. if (m_platformPlugin.plugin() && m_platformPlugin.plugin()->supportsExtension(QWebKitPlatformPlugin::Notifications))
  168. wrapper->m_presenter = m_platformPlugin.createNotificationPresenter();
  169. if (!wrapper->m_presenter) {
  170. #ifndef QT_NO_SYSTEMTRAYICON
  171. if (!dumpNotification)
  172. wrapper->m_closeTimer.startOneShot(notificationTimeout);
  173. #endif
  174. }
  175. wrapper->m_displayEventTimer.startOneShot(0);
  176. // Make sure the notification was not cancelled during handling the display event
  177. if (m_notifications.find(notification) == m_notifications.end())
  178. return;
  179. if (wrapper->m_presenter) {
  180. wrapper->connect(wrapper->m_presenter.get(), SIGNAL(notificationClosed()), wrapper, SLOT(notificationClosed()), Qt::QueuedConnection);
  181. wrapper->connect(wrapper->m_presenter.get(), SIGNAL(notificationClicked()), wrapper, SLOT(notificationClicked()));
  182. wrapper->m_presenter->showNotification(wrapper);
  183. return;
  184. }
  185. #ifndef QT_NO_SYSTEMTRAYICON
  186. wrapper->connect(m_systemTrayIcon.data(), SIGNAL(messageClicked()), wrapper, SLOT(notificationClicked()));
  187. QMetaObject::invokeMethod(m_systemTrayIcon.data(), "show");
  188. QMetaObject::invokeMethod(m_systemTrayIcon.data(), "showMessage", Q_ARG(QString, notification->title()), Q_ARG(QString, notification->body()));
  189. #endif
  190. }
  191. void NotificationPresenterClientQt::cancel(Notification* notification)
  192. {
  193. if (dumpNotification && notification->scriptExecutionContext())
  194. printf("DESKTOP NOTIFICATION CLOSED: %s\n", QString(notification->title()).toUtf8().constData());
  195. NotificationsQueue::Iterator iter = m_notifications.find(notification);
  196. if (iter != m_notifications.end()) {
  197. sendEvent(notification, eventNames().closeEvent);
  198. detachNotification(notification);
  199. }
  200. }
  201. void NotificationPresenterClientQt::cancel(NotificationWrapper* wrapper)
  202. {
  203. Notification* notification = notificationForWrapper(wrapper);
  204. if (notification)
  205. cancel(notification);
  206. }
  207. void NotificationPresenterClientQt::notificationClicked(NotificationWrapper* wrapper)
  208. {
  209. Notification* notification = notificationForWrapper(wrapper);
  210. if (notification) {
  211. // Make sure clicks on notifications are treated as user gestures.
  212. UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
  213. sendEvent(notification, eventNames().clickEvent);
  214. }
  215. }
  216. void NotificationPresenterClientQt::notificationClicked(const QString& title)
  217. {
  218. if (!dumpNotification)
  219. return;
  220. NotificationsQueue::ConstIterator end = m_notifications.end();
  221. NotificationsQueue::ConstIterator iter = m_notifications.begin();
  222. Notification* notification = 0;
  223. while (iter != end) {
  224. notification = iter.key();
  225. QString notificationTitle = notification->title();
  226. if (notificationTitle == title)
  227. break;
  228. iter++;
  229. }
  230. if (notification)
  231. sendEvent(notification, eventNames().clickEvent);
  232. }
  233. Notification* NotificationPresenterClientQt::notificationForWrapper(const NotificationWrapper* wrapper) const
  234. {
  235. NotificationsQueue::ConstIterator end = m_notifications.end();
  236. NotificationsQueue::ConstIterator iter = m_notifications.begin();
  237. while (iter != end && iter.value() != wrapper)
  238. iter++;
  239. if (iter != end)
  240. return iter.key();
  241. return 0;
  242. }
  243. void NotificationPresenterClientQt::notificationObjectDestroyed(Notification* notification)
  244. {
  245. // Called from ~Notification(), Remove the entry from the notifications list and delete the icon.
  246. NotificationsQueue::Iterator iter = m_notifications.find(notification);
  247. if (iter != m_notifications.end())
  248. delete m_notifications.take(notification);
  249. }
  250. void NotificationPresenterClientQt::notificationControllerDestroyed()
  251. {
  252. }
  253. #if ENABLE(LEGACY_NOTIFICATIONS)
  254. void NotificationPresenterClientQt::requestPermission(ScriptExecutionContext* context, PassRefPtr<VoidCallback> callback)
  255. {
  256. if (dumpNotification)
  257. printf("DESKTOP NOTIFICATION PERMISSION REQUESTED: %s\n", QString(context->securityOrigin()->toString()).toUtf8().constData());
  258. NotificationClient::Permission permission = checkPermission(context);
  259. if (permission != NotificationClient::PermissionNotAllowed) {
  260. if (callback)
  261. callback->handleEvent();
  262. return;
  263. }
  264. QHash<ScriptExecutionContext*, CallbacksInfo >::iterator iter = m_pendingPermissionRequests.find(context);
  265. if (iter != m_pendingPermissionRequests.end())
  266. iter.value().m_voidCallbacks.append(callback);
  267. else {
  268. RefPtr<VoidCallback> cb = callback;
  269. CallbacksInfo info;
  270. info.m_frame = toFrame(context);
  271. info.m_voidCallbacks.append(cb);
  272. if (toPage(context) && toFrame(context)) {
  273. m_pendingPermissionRequests.insert(context, info);
  274. toPage(context)->notificationsPermissionRequested(toFrame(context));
  275. }
  276. }
  277. }
  278. #endif
  279. #if ENABLE(NOTIFICATIONS)
  280. void NotificationPresenterClientQt::requestPermission(ScriptExecutionContext* context, PassRefPtr<NotificationPermissionCallback> callback)
  281. {
  282. if (dumpNotification)
  283. printf("DESKTOP NOTIFICATION PERMISSION REQUESTED: %s\n", QString(context->securityOrigin()->toString()).toUtf8().constData());
  284. NotificationClient::Permission permission = checkPermission(context);
  285. if (permission != NotificationClient::PermissionNotAllowed) {
  286. if (callback)
  287. callback->handleEvent(Notification::permissionString(permission));
  288. return;
  289. }
  290. QHash<ScriptExecutionContext*, CallbacksInfo >::iterator iter = m_pendingPermissionRequests.find(context);
  291. if (iter != m_pendingPermissionRequests.end())
  292. iter.value().m_callbacks.append(callback);
  293. else {
  294. RefPtr<NotificationPermissionCallback> cb = callback;
  295. CallbacksInfo info;
  296. info.m_frame = toFrame(context);
  297. info.m_callbacks.append(cb);
  298. if (toPage(context) && toFrame(context)) {
  299. m_pendingPermissionRequests.insert(context, info);
  300. toPage(context)->notificationsPermissionRequested(toFrame(context));
  301. }
  302. }
  303. }
  304. #endif
  305. NotificationClient::Permission NotificationPresenterClientQt::checkPermission(ScriptExecutionContext* context)
  306. {
  307. return m_cachedPermissions.value(context, NotificationClient::PermissionNotAllowed);
  308. }
  309. void NotificationPresenterClientQt::cancelRequestsForPermission(ScriptExecutionContext* context)
  310. {
  311. m_cachedPermissions.remove(context);
  312. QHash<ScriptExecutionContext*, CallbacksInfo >::iterator iter = m_pendingPermissionRequests.find(context);
  313. if (iter == m_pendingPermissionRequests.end())
  314. return;
  315. QWebFrameAdapter* frame = iter.value().m_frame;
  316. if (!frame)
  317. return;
  318. QWebPageAdapter* page = QWebPageAdapter::kit(frame->frame->page());
  319. m_pendingPermissionRequests.erase(iter);
  320. if (!page)
  321. return;
  322. if (dumpNotification)
  323. printf("DESKTOP NOTIFICATION PERMISSION REQUEST CANCELLED: %s\n", QString(context->securityOrigin()->toString()).toUtf8().constData());
  324. page->notificationsPermissionRequestCancelled(frame);
  325. }
  326. void NotificationPresenterClientQt::setNotificationsAllowedForFrame(Frame* frame, bool allowed)
  327. {
  328. ASSERT(frame->document());
  329. if (!frame->document())
  330. return;
  331. NotificationClient::Permission permission = allowed ? NotificationClient::PermissionAllowed : NotificationClient::PermissionDenied;
  332. m_cachedPermissions.insert(frame->document(), permission);
  333. QHash<ScriptExecutionContext*, CallbacksInfo>::iterator iter = m_pendingPermissionRequests.begin();
  334. while (iter != m_pendingPermissionRequests.end()) {
  335. if (iter.key() == frame->document())
  336. break;
  337. }
  338. if (iter == m_pendingPermissionRequests.end())
  339. return;
  340. #if ENABLE(LEGACY_NOTIFICATIONS)
  341. QList<RefPtr<VoidCallback> >& voidCallbacks = iter.value().m_voidCallbacks;
  342. Q_FOREACH(const RefPtr<VoidCallback>& callback, voidCallbacks) {
  343. if (callback)
  344. callback->handleEvent();
  345. }
  346. #endif
  347. #if ENABLE(NOTIFICATIONS)
  348. QList<RefPtr<NotificationPermissionCallback> >& callbacks = iter.value().m_callbacks;
  349. Q_FOREACH(const RefPtr<NotificationPermissionCallback>& callback, callbacks) {
  350. if (callback)
  351. callback->handleEvent(Notification::permissionString(permission));
  352. }
  353. #endif
  354. m_pendingPermissionRequests.remove(iter.key());
  355. }
  356. void NotificationPresenterClientQt::sendDisplayEvent(NotificationWrapper* wrapper)
  357. {
  358. Notification* notification = notificationForWrapper(wrapper);
  359. if (notification)
  360. sendEvent(notification, "show");
  361. }
  362. void NotificationPresenterClientQt::sendEvent(Notification* notification, const AtomicString& eventName)
  363. {
  364. if (notification->scriptExecutionContext())
  365. notification->dispatchEvent(Event::create(eventName, false, true));
  366. }
  367. void NotificationPresenterClientQt::clearCachedPermissions()
  368. {
  369. m_cachedPermissions.clear();
  370. }
  371. void NotificationPresenterClientQt::removeReplacedNotificationFromQueue(Notification* notification)
  372. {
  373. Notification* oldNotification = 0;
  374. NotificationsQueue::Iterator end = m_notifications.end();
  375. NotificationsQueue::Iterator iter = m_notifications.begin();
  376. while (iter != end) {
  377. Notification* existingNotification = iter.key();
  378. if (existingNotification->tag() == notification->tag()) {
  379. oldNotification = iter.key();
  380. break;
  381. }
  382. iter++;
  383. }
  384. if (oldNotification) {
  385. if (dumpNotification)
  386. dumpReplacedIdText(oldNotification);
  387. sendEvent(oldNotification, eventNames().closeEvent);
  388. detachNotification(oldNotification);
  389. }
  390. }
  391. void NotificationPresenterClientQt::detachNotification(Notification* notification)
  392. {
  393. delete m_notifications.take(notification);
  394. notification->detachPresenter();
  395. notification->unsetPendingActivity(notification);
  396. }
  397. void NotificationPresenterClientQt::dumpReplacedIdText(Notification* notification)
  398. {
  399. if (notification)
  400. printf("REPLACING NOTIFICATION %s\n", QString(notification->title()).toUtf8().constData());
  401. }
  402. void NotificationPresenterClientQt::dumpShowText(Notification* notification)
  403. {
  404. printf("DESKTOP NOTIFICATION:%s icon %s, title %s, text %s\n",
  405. notification->dir() == "rtl" ? "(RTL)" : "",
  406. QString(notification->iconURL().string()).toUtf8().constData(), QString(notification->title()).toUtf8().constData(),
  407. QString(notification->body()).toUtf8().constData());
  408. }
  409. QWebPageAdapter* NotificationPresenterClientQt::toPage(ScriptExecutionContext* context)
  410. {
  411. if (!context || context->isWorkerContext())
  412. return 0;
  413. Document* document = static_cast<Document*>(context);
  414. Page* page = document->page();
  415. if (!page || !page->mainFrame())
  416. return 0;
  417. return QWebPageAdapter::kit(page);
  418. }
  419. QWebFrameAdapter* NotificationPresenterClientQt::toFrame(ScriptExecutionContext* context)
  420. {
  421. if (!context || context->isWorkerContext())
  422. return 0;
  423. Document* document = static_cast<Document*>(context);
  424. if (!document || !document->frame())
  425. return 0;
  426. return QWebFrameAdapter::kit(document->frame());
  427. }
  428. #endif // ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
  429. }
  430. #include "moc_NotificationPresenterClientQt.cpp"