1/*
2 * Copyright (C) 2010 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#pragma once
27
28#include "APIError.h"
29#include "APISerializedScriptValue.h"
30#include "CallbackID.h"
31#include "ProcessThrottler.h"
32#include "ShareableBitmap.h"
33#include "WKAPICast.h"
34#include <wtf/Function.h>
35#include <wtf/HashMap.h>
36#include <wtf/MainThread.h>
37#include <wtf/RefCounted.h>
38#include <wtf/Threading.h>
39
40namespace WebKit {
41
42class CallbackBase : public RefCounted<CallbackBase> {
43public:
44 enum class Error {
45 None,
46 Unknown,
47 ProcessExited,
48 OwnerWasInvalidated,
49 };
50
51 virtual ~CallbackBase()
52 {
53 }
54
55 CallbackID callbackID() const { return m_callbackID; }
56
57 template<class T>
58 T* as()
59 {
60 if (T::type() == m_type)
61 return static_cast<T*>(this);
62
63 return nullptr;
64 }
65
66 virtual void invalidate(Error) = 0;
67
68protected:
69 struct TypeTag { };
70 typedef const TypeTag* Type;
71
72 explicit CallbackBase(Type type, const ProcessThrottler::BackgroundActivityToken& activityToken)
73 : m_type(type)
74 , m_callbackID(CallbackID::generateID())
75 , m_activityToken(activityToken)
76 {
77 }
78
79private:
80 Type m_type;
81 CallbackID m_callbackID;
82 ProcessThrottler::BackgroundActivityToken m_activityToken;
83};
84
85template<typename... T>
86class GenericCallback : public CallbackBase {
87public:
88 typedef Function<void (T..., Error)> CallbackFunction;
89
90 static Ref<GenericCallback> create(CallbackFunction&& callback, const ProcessThrottler::BackgroundActivityToken& activityToken = nullptr)
91 {
92 return adoptRef(*new GenericCallback(WTFMove(callback), activityToken));
93 }
94
95 virtual ~GenericCallback()
96 {
97 ASSERT(m_originThread.ptr() == &Thread::current());
98 ASSERT(!m_callback);
99 }
100
101 void performCallbackWithReturnValue(T... returnValue)
102 {
103 ASSERT(m_originThread.ptr() == &Thread::current());
104
105 if (!m_callback)
106 return;
107
108 auto callback = std::exchange(m_callback, WTF::nullopt);
109 callback.value()(returnValue..., Error::None);
110 }
111
112 void performCallback()
113 {
114 performCallbackWithReturnValue();
115 }
116
117 void invalidate(Error error = Error::Unknown) final
118 {
119 ASSERT(m_originThread.ptr() == &Thread::current());
120
121 if (!m_callback)
122 return;
123
124 auto callback = std::exchange(m_callback, WTF::nullopt);
125 callback.value()(typename std::remove_reference<T>::type()..., error);
126 }
127
128private:
129 GenericCallback(CallbackFunction&& callback, const ProcessThrottler::BackgroundActivityToken& activityToken)
130 : CallbackBase(type(), activityToken)
131 , m_callback(WTFMove(callback))
132 {
133 }
134
135 friend class CallbackBase;
136 static Type type()
137 {
138 static TypeTag tag;
139 return &tag;
140 }
141
142 Optional<CallbackFunction> m_callback;
143
144#ifndef NDEBUG
145 Ref<Thread> m_originThread { Thread::current() };
146#endif
147};
148
149template<typename APIReturnValueType, typename InternalReturnValueType = typename APITypeInfo<APIReturnValueType>::ImplType*>
150static typename GenericCallback<InternalReturnValueType>::CallbackFunction toGenericCallbackFunction(void* context, void (*callback)(APIReturnValueType, WKErrorRef, void*))
151{
152 return [context, callback](InternalReturnValueType returnValue, CallbackBase::Error error) {
153 callback(toAPI(returnValue), error != CallbackBase::Error::None ? toAPI(API::Error::create().ptr()) : 0, context);
154 };
155}
156
157typedef GenericCallback<> VoidCallback;
158typedef GenericCallback<const Vector<WebCore::IntRect>&, double, WebCore::FloatBoxExtent> ComputedPagesCallback;
159typedef GenericCallback<const ShareableBitmap::Handle&> ImageCallback;
160
161template<typename T>
162void invalidateCallbackMap(HashMap<uint64_t, T>& callbackMap, CallbackBase::Error error)
163{
164 auto map = WTFMove(callbackMap);
165 for (auto& callback : map.values())
166 callback->invalidate(error);
167}
168
169class CallbackMap {
170public:
171 CallbackID put(Ref<CallbackBase>&& callback)
172 {
173 RELEASE_ASSERT(RunLoop::isMain());
174 auto callbackID = callback->callbackID();
175 RELEASE_ASSERT(callbackID.isValid());
176 RELEASE_ASSERT(!m_map.contains(callbackID.m_id));
177 m_map.set(callbackID.m_id, WTFMove(callback));
178 return callbackID;
179 }
180
181 template<unsigned I, typename T, typename... U>
182 struct GenericCallbackType {
183 typedef typename GenericCallbackType<I - 1, U..., T>::type type;
184 };
185
186 template<typename... U>
187 struct GenericCallbackType<1, CallbackBase::Error, U...> {
188 typedef GenericCallback<U...> type;
189 };
190
191 template<typename... T>
192 CallbackID put(Function<void(T...)>&& function, const ProcessThrottler::BackgroundActivityToken& activityToken)
193 {
194 auto callback = GenericCallbackType<sizeof...(T), T...>::type::create(WTFMove(function), activityToken);
195 return put(WTFMove(callback));
196 }
197
198 template<class T>
199 RefPtr<T> take(CallbackID callbackID)
200 {
201 RELEASE_ASSERT(callbackID.isValid());
202 RELEASE_ASSERT(RunLoop::isMain());
203 auto base = m_map.take(callbackID.m_id);
204 if (!base)
205 return nullptr;
206
207 return adoptRef(base.leakRef()->as<T>());
208 }
209
210 void invalidate(CallbackBase::Error error)
211 {
212 RELEASE_ASSERT(RunLoop::isMain());
213 invalidateCallbackMap(m_map, error);
214 }
215
216private:
217 HashMap<uint64_t, RefPtr<CallbackBase>> m_map;
218};
219
220} // namespace WebKit
221