| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "modules/notifications/ServiceWorkerRegistrationNotifications.h" |
| |
| #include "bindings/core/v8/CallbackPromiseAdapter.h" |
| #include "bindings/core/v8/ExceptionState.h" |
| #include "bindings/core/v8/ScriptPromiseResolver.h" |
| #include "core/dom/ExecutionContext.h" |
| #include "modules/notifications/GetNotificationOptions.h" |
| #include "modules/notifications/Notification.h" |
| #include "modules/notifications/NotificationData.h" |
| #include "modules/notifications/NotificationManager.h" |
| #include "modules/notifications/NotificationOptions.h" |
| #include "modules/notifications/NotificationResourcesLoader.h" |
| #include "modules/serviceworkers/ServiceWorkerRegistration.h" |
| #include "platform/Histogram.h" |
| #include "platform/heap/Handle.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebSecurityOrigin.h" |
| #include "public/platform/modules/notifications/WebNotificationData.h" |
| #include "wtf/Assertions.h" |
| #include "wtf/PtrUtil.h" |
| #include "wtf/RefPtr.h" |
| #include <memory> |
| #include <utility> |
| |
| namespace blink { |
| namespace { |
| |
| // Allows using a CallbackPromiseAdapter with a WebVector to resolve the |
| // getNotifications() promise with a HeapVector owning Notifications. |
| class NotificationArray { |
| public: |
| using WebType = const WebVector<WebPersistentNotificationInfo>&; |
| |
| static HeapVector<Member<Notification>> take( |
| ScriptPromiseResolver* resolver, |
| const WebVector<WebPersistentNotificationInfo>& notificationInfos) { |
| HeapVector<Member<Notification>> notifications; |
| for (const WebPersistentNotificationInfo& notificationInfo : |
| notificationInfos) |
| notifications.push_back(Notification::create( |
| resolver->getExecutionContext(), notificationInfo.notificationId, |
| notificationInfo.data, true /* showing */)); |
| |
| return notifications; |
| } |
| |
| private: |
| NotificationArray() = delete; |
| }; |
| |
| } // namespace |
| |
| ServiceWorkerRegistrationNotifications::ServiceWorkerRegistrationNotifications( |
| ExecutionContext* executionContext, |
| ServiceWorkerRegistration* registration) |
| : ContextLifecycleObserver(executionContext), |
| m_registration(registration) {} |
| |
| ScriptPromise ServiceWorkerRegistrationNotifications::showNotification( |
| ScriptState* scriptState, |
| ServiceWorkerRegistration& registration, |
| const String& title, |
| const NotificationOptions& options, |
| ExceptionState& exceptionState) { |
| ExecutionContext* executionContext = scriptState->getExecutionContext(); |
| |
| // If context object's active worker is null, reject the promise with a |
| // TypeError exception. |
| if (!registration.active()) |
| return ScriptPromise::reject( |
| scriptState, |
| V8ThrowException::createTypeError(scriptState->isolate(), |
| "No active registration available on " |
| "the ServiceWorkerRegistration.")); |
| |
| // If permission for notification's origin is not "granted", reject the |
| // promise with a TypeError exception, and terminate these substeps. |
| if (NotificationManager::from(executionContext) |
| ->permissionStatus(executionContext) != |
| mojom::blink::PermissionStatus::GRANTED) |
| return ScriptPromise::reject( |
| scriptState, |
| V8ThrowException::createTypeError( |
| scriptState->isolate(), |
| "No notification permission has been granted for this origin.")); |
| |
| // Validate the developer-provided options to get the WebNotificationData. |
| WebNotificationData data = createWebNotificationData(executionContext, title, |
| options, exceptionState); |
| if (exceptionState.hadException()) |
| return exceptionState.reject(scriptState); |
| |
| // Log number of actions developer provided in linear histogram: |
| // 0 -> underflow bucket, |
| // 1-16 -> distinct buckets, |
| // 17+ -> overflow bucket. |
| DEFINE_THREAD_SAFE_STATIC_LOCAL( |
| EnumerationHistogram, notificationCountHistogram, |
| new EnumerationHistogram( |
| "Notifications.PersistentNotificationActionCount", 17)); |
| notificationCountHistogram.count(options.actions().size()); |
| |
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| ScriptPromise promise = resolver->promise(); |
| |
| std::unique_ptr<WebNotificationShowCallbacks> callbacks = |
| WTF::wrapUnique(new CallbackPromiseAdapter<void, void>(resolver)); |
| ServiceWorkerRegistrationNotifications::from(executionContext, registration) |
| .prepareShow(data, std::move(callbacks)); |
| |
| return promise; |
| } |
| |
| ScriptPromise ServiceWorkerRegistrationNotifications::getNotifications( |
| ScriptState* scriptState, |
| ServiceWorkerRegistration& registration, |
| const GetNotificationOptions& options) { |
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| ScriptPromise promise = resolver->promise(); |
| |
| auto callbacks = |
| WTF::makeUnique<CallbackPromiseAdapter<NotificationArray, void>>( |
| resolver); |
| |
| WebNotificationManager* notificationManager = |
| Platform::current()->notificationManager(); |
| DCHECK(notificationManager); |
| |
| notificationManager->getNotifications( |
| options.tag(), registration.webRegistration(), std::move(callbacks)); |
| return promise; |
| } |
| |
| void ServiceWorkerRegistrationNotifications::contextDestroyed( |
| ExecutionContext*) { |
| for (auto loader : m_loaders) |
| loader->stop(); |
| } |
| |
| DEFINE_TRACE(ServiceWorkerRegistrationNotifications) { |
| visitor->trace(m_registration); |
| visitor->trace(m_loaders); |
| Supplement<ServiceWorkerRegistration>::trace(visitor); |
| ContextLifecycleObserver::trace(visitor); |
| } |
| |
| const char* ServiceWorkerRegistrationNotifications::supplementName() { |
| return "ServiceWorkerRegistrationNotifications"; |
| } |
| |
| ServiceWorkerRegistrationNotifications& |
| ServiceWorkerRegistrationNotifications::from( |
| ExecutionContext* executionContext, |
| ServiceWorkerRegistration& registration) { |
| ServiceWorkerRegistrationNotifications* supplement = |
| static_cast<ServiceWorkerRegistrationNotifications*>( |
| Supplement<ServiceWorkerRegistration>::from(registration, |
| supplementName())); |
| if (!supplement) { |
| supplement = new ServiceWorkerRegistrationNotifications(executionContext, |
| ®istration); |
| provideTo(registration, supplementName(), supplement); |
| } |
| return *supplement; |
| } |
| |
| void ServiceWorkerRegistrationNotifications::prepareShow( |
| const WebNotificationData& data, |
| std::unique_ptr<WebNotificationShowCallbacks> callbacks) { |
| RefPtr<SecurityOrigin> origin = getExecutionContext()->getSecurityOrigin(); |
| NotificationResourcesLoader* loader = new NotificationResourcesLoader( |
| WTF::bind(&ServiceWorkerRegistrationNotifications::didLoadResources, |
| wrapWeakPersistent(this), origin.release(), data, |
| WTF::passed(std::move(callbacks)))); |
| m_loaders.add(loader); |
| loader->start(getExecutionContext(), data); |
| } |
| |
| void ServiceWorkerRegistrationNotifications::didLoadResources( |
| PassRefPtr<SecurityOrigin> origin, |
| const WebNotificationData& data, |
| std::unique_ptr<WebNotificationShowCallbacks> callbacks, |
| NotificationResourcesLoader* loader) { |
| DCHECK(m_loaders.contains(loader)); |
| |
| WebNotificationManager* notificationManager = |
| Platform::current()->notificationManager(); |
| DCHECK(notificationManager); |
| |
| notificationManager->showPersistent( |
| WebSecurityOrigin(origin.get()), data, loader->getResources(), |
| m_registration->webRegistration(), std::move(callbacks)); |
| m_loaders.remove(loader); |
| } |
| |
| } // namespace blink |