4c1bc6f839229a7f733fdf593af52d47a9e018ed
[WebKit-https.git] / Source / WebCore / workers / service / ExtendableEvent.cpp
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
35 namespace WebCore {
36
37 ExtendableEvent::ExtendableEvent(const AtomString& type, const ExtendableEventInit& initializer, IsTrusted isTrusted)
38     : Event(type, initializer, isTrusted)
39 {
40 }
41
42 ExtendableEvent::ExtendableEvent(const AtomString& type, CanBubble canBubble, IsCancelable cancelable)
43     : Event(type, canBubble, cancelable)
44 {
45 }
46
47 ExtendableEvent::~ExtendableEvent()
48 {
49 }
50
51 // https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
52 ExceptionOr<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
65 class FunctionMicrotask final : public JSC::Microtask {
66 public:
67     static Ref<FunctionMicrotask> create(Function<void()>&& function)
68     {
69         return adoptRef(*new FunctionMicrotask(WTFMove(function)));
70     }
71
72 private:
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
86 void 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
115 void 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)