1/*
2 * Copyright (C) 2011 Google 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 are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "ThreadableWebSocketChannelClientWrapper.h"
33
34#include "ScriptExecutionContext.h"
35#include "WebSocketChannelClient.h"
36#include <wtf/RefPtr.h>
37#include <wtf/text/StringView.h>
38
39namespace WebCore {
40
41ThreadableWebSocketChannelClientWrapper::ThreadableWebSocketChannelClientWrapper(ScriptExecutionContext& context, WebSocketChannelClient& client)
42 : m_context(context)
43 , m_client(&client)
44 , m_peer(nullptr)
45 , m_failedWebSocketChannelCreation(false)
46 , m_syncMethodDone(true)
47 , m_sendRequestResult(ThreadableWebSocketChannel::SendFail)
48 , m_bufferedAmount(0)
49 , m_suspended(false)
50{
51}
52
53Ref<ThreadableWebSocketChannelClientWrapper> ThreadableWebSocketChannelClientWrapper::create(ScriptExecutionContext& context, WebSocketChannelClient& client)
54{
55 return adoptRef(*new ThreadableWebSocketChannelClientWrapper(context, client));
56}
57
58void ThreadableWebSocketChannelClientWrapper::clearSyncMethodDone()
59{
60 m_syncMethodDone = false;
61}
62
63void ThreadableWebSocketChannelClientWrapper::setSyncMethodDone()
64{
65 m_syncMethodDone = true;
66}
67
68bool ThreadableWebSocketChannelClientWrapper::syncMethodDone() const
69{
70 return m_syncMethodDone;
71}
72
73WorkerThreadableWebSocketChannel::Peer* ThreadableWebSocketChannelClientWrapper::peer() const
74{
75 return m_peer;
76}
77
78void ThreadableWebSocketChannelClientWrapper::didCreateWebSocketChannel(WorkerThreadableWebSocketChannel::Peer* peer)
79{
80 m_peer = peer;
81 m_syncMethodDone = true;
82}
83
84void ThreadableWebSocketChannelClientWrapper::clearPeer()
85{
86 m_peer = nullptr;
87}
88
89bool ThreadableWebSocketChannelClientWrapper::failedWebSocketChannelCreation() const
90{
91 return m_failedWebSocketChannelCreation;
92}
93
94void ThreadableWebSocketChannelClientWrapper::setFailedWebSocketChannelCreation()
95{
96 m_failedWebSocketChannelCreation = true;
97}
98
99String ThreadableWebSocketChannelClientWrapper::subprotocol() const
100{
101 if (m_subprotocol.isEmpty())
102 return emptyString();
103 return String(m_subprotocol);
104}
105
106void ThreadableWebSocketChannelClientWrapper::setSubprotocol(const String& subprotocol)
107{
108 unsigned length = subprotocol.length();
109 m_subprotocol.resize(length);
110 StringView(subprotocol).getCharactersWithUpconvert(m_subprotocol.data());
111}
112
113String ThreadableWebSocketChannelClientWrapper::extensions() const
114{
115 if (m_extensions.isEmpty())
116 return emptyString();
117 return String(m_extensions);
118}
119
120void ThreadableWebSocketChannelClientWrapper::setExtensions(const String& extensions)
121{
122 unsigned length = extensions.length();
123 m_extensions.resize(length);
124 StringView(extensions).getCharactersWithUpconvert(m_extensions.data());
125}
126
127ThreadableWebSocketChannel::SendResult ThreadableWebSocketChannelClientWrapper::sendRequestResult() const
128{
129 return m_sendRequestResult;
130}
131
132void ThreadableWebSocketChannelClientWrapper::setSendRequestResult(ThreadableWebSocketChannel::SendResult sendRequestResult)
133{
134 m_sendRequestResult = sendRequestResult;
135 m_syncMethodDone = true;
136}
137
138unsigned ThreadableWebSocketChannelClientWrapper::bufferedAmount() const
139{
140 return m_bufferedAmount;
141}
142
143void ThreadableWebSocketChannelClientWrapper::setBufferedAmount(unsigned bufferedAmount)
144{
145 m_bufferedAmount = bufferedAmount;
146 m_syncMethodDone = true;
147}
148
149void ThreadableWebSocketChannelClientWrapper::clearClient()
150{
151 m_client = nullptr;
152}
153
154void ThreadableWebSocketChannelClientWrapper::didConnect()
155{
156 m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this)] (ScriptExecutionContext&) {
157 if (m_client)
158 m_client->didConnect();
159 }));
160
161 if (!m_suspended)
162 processPendingTasks();
163}
164
165void ThreadableWebSocketChannelClientWrapper::didReceiveMessage(const String& message)
166{
167 m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this), message = message.isolatedCopy()] (ScriptExecutionContext&) {
168 if (m_client)
169 m_client->didReceiveMessage(message);
170 }));
171
172 if (!m_suspended)
173 processPendingTasks();
174}
175
176void ThreadableWebSocketChannelClientWrapper::didReceiveBinaryData(Vector<uint8_t>&& binaryData)
177{
178 m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this), binaryData = WTFMove(binaryData)] (ScriptExecutionContext&) mutable {
179 if (m_client)
180 m_client->didReceiveBinaryData(WTFMove(binaryData));
181 }));
182
183 if (!m_suspended)
184 processPendingTasks();
185}
186
187void ThreadableWebSocketChannelClientWrapper::didUpdateBufferedAmount(unsigned bufferedAmount)
188{
189 m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this), bufferedAmount] (ScriptExecutionContext&) {
190 if (m_client)
191 m_client->didUpdateBufferedAmount(bufferedAmount);
192 }));
193
194 if (!m_suspended)
195 processPendingTasks();
196}
197
198void ThreadableWebSocketChannelClientWrapper::didStartClosingHandshake()
199{
200 m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this)] (ScriptExecutionContext&) {
201 if (m_client)
202 m_client->didStartClosingHandshake();
203 }));
204
205 if (!m_suspended)
206 processPendingTasks();
207}
208
209void ThreadableWebSocketChannelClientWrapper::didClose(unsigned unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
210{
211 m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this), unhandledBufferedAmount, closingHandshakeCompletion, code, reason = reason.isolatedCopy()] (ScriptExecutionContext&) {
212 if (m_client)
213 m_client->didClose(unhandledBufferedAmount, closingHandshakeCompletion, code, reason);
214 }));
215
216 if (!m_suspended)
217 processPendingTasks();
218}
219
220void ThreadableWebSocketChannelClientWrapper::didReceiveMessageError()
221{
222 m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this)] (ScriptExecutionContext&) {
223 if (m_client)
224 m_client->didReceiveMessageError();
225 }));
226
227 if (!m_suspended)
228 processPendingTasks();
229}
230
231void ThreadableWebSocketChannelClientWrapper::didUpgradeURL()
232{
233 m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this)] (ScriptExecutionContext&) {
234 if (m_client)
235 m_client->didUpgradeURL();
236 }));
237
238 if (!m_suspended)
239 processPendingTasks();
240}
241
242void ThreadableWebSocketChannelClientWrapper::suspend()
243{
244 m_suspended = true;
245}
246
247void ThreadableWebSocketChannelClientWrapper::resume()
248{
249 m_suspended = false;
250 processPendingTasks();
251}
252
253void ThreadableWebSocketChannelClientWrapper::processPendingTasks()
254{
255 if (m_suspended)
256 return;
257 if (!m_syncMethodDone) {
258 // When a synchronous operation is in progress (i.e. the execution stack contains
259 // WorkerThreadableWebSocketChannel::waitForMethodCompletion()), we cannot invoke callbacks in this run loop.
260 m_context.postTask([this, protectedThis = makeRef(*this)] (ScriptExecutionContext& context) {
261 ASSERT_UNUSED(context, context.isWorkerGlobalScope());
262 processPendingTasks();
263 });
264 return;
265 }
266
267 Vector<std::unique_ptr<ScriptExecutionContext::Task>> pendingTasks = WTFMove(m_pendingTasks);
268 for (auto& task : pendingTasks)
269 task->performTask(m_context);
270}
271
272} // namespace WebCore
273