1/*
2 * Copyright (C) 2017 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ExtendableEvent.h"
28
29#if ENABLE(SERVICE_WORKER)
30
31#include "JSDOMPromise.h"
32#include "ScriptExecutionContext.h"
33#include <JavaScriptCore/Microtask.h>
34
35namespace WebCore {
36
37ExtendableEvent::ExtendableEvent(const AtomicString& type, const ExtendableEventInit& initializer, IsTrusted isTrusted)
38 : Event(type, initializer, isTrusted)
39{
40}
41
42ExtendableEvent::ExtendableEvent(const AtomicString& type, CanBubble canBubble, IsCancelable cancelable)
43 : Event(type, canBubble, cancelable)
44{
45}
46
47ExtendableEvent::~ExtendableEvent()
48{
49}
50
51// https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
52ExceptionOr<void> ExtendableEvent::waitUntil(Ref<DOMPromise>&& promise)
53{
54 if (!isTrusted())
55 return Exception { InvalidStateError, "Event is not trusted"_s };
56
57 // If the pending promises count is zero and the dispatch flag is unset, throw an "InvalidStateError" DOMException.
58 if (!m_pendingPromiseCount && !isBeingDispatched())
59 return Exception { InvalidStateError, "Event is no longer being dispatched and has no pending promises"_s };
60
61 addExtendLifetimePromise(WTFMove(promise));
62 return { };
63}
64
65class FunctionMicrotask final : public JSC::Microtask {
66public:
67 static Ref<FunctionMicrotask> create(Function<void()>&& function)
68 {
69 return adoptRef(*new FunctionMicrotask(WTFMove(function)));
70 }
71
72private:
73 explicit FunctionMicrotask(Function<void()>&& function)
74 : m_function(WTFMove(function))
75 {
76 }
77
78 void run(JSC::ExecState*) final
79 {
80 m_function();
81 }
82
83 Function<void()> m_function;
84};
85
86void ExtendableEvent::addExtendLifetimePromise(Ref<DOMPromise>&& promise)
87{
88 promise->whenSettled([this, protectedThis = makeRefPtr(this), settledPromise = promise.ptr()] () mutable {
89 auto& globalObject = *settledPromise->globalObject();
90 globalObject.queueMicrotask(FunctionMicrotask::create([this, protectedThis = WTFMove(protectedThis), settledPromise = WTFMove(settledPromise)] () mutable {
91 --m_pendingPromiseCount;
92
93 // FIXME: Let registration be the context object's relevant global object's associated service worker's containing service worker registration.
94 // FIXME: If registration's uninstalling flag is set, invoke Try Clear Registration with registration.
95 // FIXME: If registration is not null, invoke Try Activate with registration.
96
97 auto* context = settledPromise->globalObject()->scriptExecutionContext();
98 if (!context)
99 return;
100 context->postTask([this, protectedThis = WTFMove(protectedThis)] (ScriptExecutionContext&) mutable {
101 if (m_pendingPromiseCount)
102 return;
103
104 auto settledPromises = WTFMove(m_extendLifetimePromises);
105 if (auto handler = WTFMove(m_whenAllExtendLifetimePromisesAreSettledHandler))
106 handler(WTFMove(settledPromises));
107 });
108 }));
109 });
110
111 m_extendLifetimePromises.add(WTFMove(promise));
112 ++m_pendingPromiseCount;
113}
114
115void ExtendableEvent::whenAllExtendLifetimePromisesAreSettled(WTF::Function<void(HashSet<Ref<DOMPromise>>&&)>&& handler)
116{
117 ASSERT_WITH_MESSAGE(target(), "Event has not been dispatched yet");
118 ASSERT(!m_whenAllExtendLifetimePromisesAreSettledHandler);
119
120 if (!m_pendingPromiseCount) {
121 handler(WTFMove(m_extendLifetimePromises));
122 return;
123 }
124
125 m_whenAllExtendLifetimePromisesAreSettledHandler = WTFMove(handler);
126}
127
128} // namespace WebCore
129
130#endif // ENABLE(SERVICE_WORKER)
131