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#if ENABLE(WEB_SOCKETS)
33#include "ThreadableWebSocketChannelClientWrapper.h"
34
35#include "CrossThreadTask.h"
36#include "ScriptExecutionContext.h"
37#include "WebSocketChannelClient.h"
38#include <wtf/PassRefPtr.h>
39#include <wtf/RefPtr.h>
40#include <wtf/text/StringView.h>
41
42namespace WebCore {
43
44ThreadableWebSocketChannelClientWrapper::ThreadableWebSocketChannelClientWrapper(ScriptExecutionContext* context, WebSocketChannelClient* client)
45    : m_context(context)
46    , m_client(client)
47    , m_peer(0)
48    , m_failedWebSocketChannelCreation(false)
49    , m_syncMethodDone(true)
50    , m_sendRequestResult(ThreadableWebSocketChannel::SendFail)
51    , m_bufferedAmount(0)
52    , m_suspended(false)
53{
54}
55
56PassRefPtr<ThreadableWebSocketChannelClientWrapper> ThreadableWebSocketChannelClientWrapper::create(ScriptExecutionContext* context, WebSocketChannelClient* client)
57{
58    return adoptRef(new ThreadableWebSocketChannelClientWrapper(context, client));
59}
60
61void ThreadableWebSocketChannelClientWrapper::clearSyncMethodDone()
62{
63    m_syncMethodDone = false;
64}
65
66void ThreadableWebSocketChannelClientWrapper::setSyncMethodDone()
67{
68    m_syncMethodDone = true;
69}
70
71bool ThreadableWebSocketChannelClientWrapper::syncMethodDone() const
72{
73    return m_syncMethodDone;
74}
75
76WorkerThreadableWebSocketChannel::Peer* ThreadableWebSocketChannelClientWrapper::peer() const
77{
78    return m_peer;
79}
80
81void ThreadableWebSocketChannelClientWrapper::didCreateWebSocketChannel(WorkerThreadableWebSocketChannel::Peer* peer)
82{
83    m_peer = peer;
84    m_syncMethodDone = true;
85}
86
87void ThreadableWebSocketChannelClientWrapper::clearPeer()
88{
89    m_peer = 0;
90}
91
92bool ThreadableWebSocketChannelClientWrapper::failedWebSocketChannelCreation() const
93{
94    return m_failedWebSocketChannelCreation;
95}
96
97void ThreadableWebSocketChannelClientWrapper::setFailedWebSocketChannelCreation()
98{
99    m_failedWebSocketChannelCreation = true;
100}
101
102String ThreadableWebSocketChannelClientWrapper::subprotocol() const
103{
104    if (m_subprotocol.isEmpty())
105        return emptyString();
106    return String(m_subprotocol);
107}
108
109void ThreadableWebSocketChannelClientWrapper::setSubprotocol(const String& subprotocol)
110{
111    unsigned length = subprotocol.length();
112    m_subprotocol.resize(length);
113    StringView(subprotocol).getCharactersWithUpconvert(m_subprotocol.data());
114}
115
116String ThreadableWebSocketChannelClientWrapper::extensions() const
117{
118    if (m_extensions.isEmpty())
119        return emptyString();
120    return String(m_extensions);
121}
122
123void ThreadableWebSocketChannelClientWrapper::setExtensions(const String& extensions)
124{
125    unsigned length = extensions.length();
126    m_extensions.resize(length);
127    StringView(extensions).getCharactersWithUpconvert(m_extensions.data());
128}
129
130ThreadableWebSocketChannel::SendResult ThreadableWebSocketChannelClientWrapper::sendRequestResult() const
131{
132    return m_sendRequestResult;
133}
134
135void ThreadableWebSocketChannelClientWrapper::setSendRequestResult(ThreadableWebSocketChannel::SendResult sendRequestResult)
136{
137    m_sendRequestResult = sendRequestResult;
138    m_syncMethodDone = true;
139}
140
141unsigned long ThreadableWebSocketChannelClientWrapper::bufferedAmount() const
142{
143    return m_bufferedAmount;
144}
145
146void ThreadableWebSocketChannelClientWrapper::setBufferedAmount(unsigned long bufferedAmount)
147{
148    m_bufferedAmount = bufferedAmount;
149    m_syncMethodDone = true;
150}
151
152void ThreadableWebSocketChannelClientWrapper::clearClient()
153{
154    m_client = 0;
155}
156
157void ThreadableWebSocketChannelClientWrapper::didConnect()
158{
159    m_pendingTasks.append(std::make_unique<CrossThreadTask>(&didConnectCallback, this));
160    if (!m_suspended)
161        processPendingTasks();
162}
163
164void ThreadableWebSocketChannelClientWrapper::didReceiveMessage(const String& message)
165{
166    m_pendingTasks.append(std::make_unique<CrossThreadTask>(&didReceiveMessageCallback, this, message));
167    if (!m_suspended)
168        processPendingTasks();
169}
170
171void ThreadableWebSocketChannelClientWrapper::didReceiveBinaryData(PassOwnPtr<Vector<char>> binaryData)
172{
173    m_pendingTasks.append(std::make_unique<CrossThreadTask>(&didReceiveBinaryDataCallback, this, binaryData));
174    if (!m_suspended)
175        processPendingTasks();
176}
177
178void ThreadableWebSocketChannelClientWrapper::didUpdateBufferedAmount(unsigned long bufferedAmount)
179{
180    m_pendingTasks.append(std::make_unique<CrossThreadTask>(&didUpdateBufferedAmountCallback, this, bufferedAmount));
181    if (!m_suspended)
182        processPendingTasks();
183}
184
185void ThreadableWebSocketChannelClientWrapper::didStartClosingHandshake()
186{
187    m_pendingTasks.append(std::make_unique<CrossThreadTask>(&didStartClosingHandshakeCallback, this));
188    if (!m_suspended)
189        processPendingTasks();
190}
191
192void ThreadableWebSocketChannelClientWrapper::didClose(unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
193{
194    m_pendingTasks.append(std::make_unique<CrossThreadTask>(&didCloseCallback, this, unhandledBufferedAmount, closingHandshakeCompletion, code, reason));
195    if (!m_suspended)
196        processPendingTasks();
197}
198
199void ThreadableWebSocketChannelClientWrapper::didReceiveMessageError()
200{
201    m_pendingTasks.append(std::make_unique<CrossThreadTask>(&didReceiveMessageErrorCallback, this));
202    if (!m_suspended)
203        processPendingTasks();
204}
205
206void ThreadableWebSocketChannelClientWrapper::suspend()
207{
208    m_suspended = true;
209}
210
211void ThreadableWebSocketChannelClientWrapper::resume()
212{
213    m_suspended = false;
214    processPendingTasks();
215}
216
217void ThreadableWebSocketChannelClientWrapper::processPendingTasksCallback(ScriptExecutionContext& context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
218{
219    ASSERT_UNUSED(context, context.isWorkerGlobalScope());
220    wrapper->processPendingTasks();
221}
222
223void ThreadableWebSocketChannelClientWrapper::processPendingTasks()
224{
225    if (m_suspended)
226        return;
227    if (!m_syncMethodDone) {
228        // When a synchronous operation is in progress (i.e. the execution stack contains
229        // WorkerThreadableWebSocketChannel::waitForMethodCompletion()), we cannot invoke callbacks in this run loop.
230        m_context->postTask(CrossThreadTask(&ThreadableWebSocketChannelClientWrapper::processPendingTasksCallback, this));
231        return;
232    }
233
234    Vector<std::unique_ptr<ScriptExecutionContext::Task>> pendingTasks = WTF::move(m_pendingTasks);
235    for (auto& task : pendingTasks)
236        task->performTask(*m_context);
237}
238
239void ThreadableWebSocketChannelClientWrapper::didConnectCallback(ScriptExecutionContext& context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
240{
241    UNUSED_PARAM(context);
242    if (wrapper->m_client)
243        wrapper->m_client->didConnect();
244}
245
246void ThreadableWebSocketChannelClientWrapper::didReceiveMessageCallback(ScriptExecutionContext& context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, const String& message)
247{
248    UNUSED_PARAM(context);
249    if (wrapper->m_client)
250        wrapper->m_client->didReceiveMessage(message);
251}
252
253void ThreadableWebSocketChannelClientWrapper::didReceiveBinaryDataCallback(ScriptExecutionContext& context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, PassOwnPtr<Vector<char>> binaryData)
254{
255    UNUSED_PARAM(context);
256    if (wrapper->m_client)
257        wrapper->m_client->didReceiveBinaryData(binaryData);
258}
259
260void ThreadableWebSocketChannelClientWrapper::didUpdateBufferedAmountCallback(ScriptExecutionContext& context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, unsigned long bufferedAmount)
261{
262    UNUSED_PARAM(context);
263    if (wrapper->m_client)
264        wrapper->m_client->didUpdateBufferedAmount(bufferedAmount);
265}
266
267void ThreadableWebSocketChannelClientWrapper::didStartClosingHandshakeCallback(ScriptExecutionContext& context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
268{
269    UNUSED_PARAM(context);
270    if (wrapper->m_client)
271        wrapper->m_client->didStartClosingHandshake();
272}
273
274void ThreadableWebSocketChannelClientWrapper::didCloseCallback(ScriptExecutionContext& context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
275{
276    UNUSED_PARAM(context);
277    if (wrapper->m_client)
278        wrapper->m_client->didClose(unhandledBufferedAmount, closingHandshakeCompletion, code, reason);
279}
280
281void ThreadableWebSocketChannelClientWrapper::didReceiveMessageErrorCallback(ScriptExecutionContext& context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
282{
283    UNUSED_PARAM(context);
284    if (wrapper->m_client)
285        wrapper->m_client->didReceiveMessageError();
286}
287
288} // namespace WebCore
289
290#endif
291