1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 * Copyright (C) 2017 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "RTCDataChannel.h"
28
29#if ENABLE(WEB_RTC)
30
31#include "Blob.h"
32#include "Event.h"
33#include "EventNames.h"
34#include "MessageEvent.h"
35#include "RTCDataChannelHandler.h"
36#include "ScriptExecutionContext.h"
37#include "SharedBuffer.h"
38#include <JavaScriptCore/ArrayBuffer.h>
39#include <JavaScriptCore/ArrayBufferView.h>
40#include <wtf/IsoMallocInlines.h>
41#include <wtf/NeverDestroyed.h>
42
43namespace WebCore {
44
45WTF_MAKE_ISO_ALLOCATED_IMPL(RTCDataChannel);
46
47static const AtomString& blobKeyword()
48{
49 static NeverDestroyed<AtomString> blob("blob", AtomString::ConstructFromLiteral);
50 return blob;
51}
52
53static const AtomString& arraybufferKeyword()
54{
55 static NeverDestroyed<AtomString> arraybuffer("arraybuffer", AtomString::ConstructFromLiteral);
56 return arraybuffer;
57}
58
59Ref<RTCDataChannel> RTCDataChannel::create(ScriptExecutionContext& context, std::unique_ptr<RTCDataChannelHandler>&& handler, String&& label, RTCDataChannelInit&& options)
60{
61 ASSERT(handler);
62 auto channel = adoptRef(*new RTCDataChannel(context, WTFMove(handler), WTFMove(label), WTFMove(options)));
63 channel->suspendIfNeeded();
64 channel->m_handler->setClient(channel.get());
65 channel->setPendingActivity(channel.get());
66 return channel;
67}
68
69RTCDataChannel::RTCDataChannel(ScriptExecutionContext& context, std::unique_ptr<RTCDataChannelHandler>&& handler, String&& label, RTCDataChannelInit&& options)
70 : ActiveDOMObject(&context)
71 , m_handler(WTFMove(handler))
72 , m_scheduledEventTimer(*this, &RTCDataChannel::scheduledEventTimerFired)
73 , m_label(WTFMove(label))
74 , m_options(WTFMove(options))
75{
76}
77
78size_t RTCDataChannel::bufferedAmount() const
79{
80 // FIXME: We should compute our own bufferedAmount and not count on m_handler which is made null at closing time.
81 if (m_stopped)
82 return 0;
83 return m_handler->bufferedAmount();
84}
85
86const AtomString& RTCDataChannel::binaryType() const
87{
88 switch (m_binaryType) {
89 case BinaryType::Blob:
90 return blobKeyword();
91 case BinaryType::ArrayBuffer:
92 return arraybufferKeyword();
93 }
94
95 ASSERT_NOT_REACHED();
96 return emptyAtom();
97}
98
99ExceptionOr<void> RTCDataChannel::setBinaryType(const AtomString& binaryType)
100{
101 if (binaryType == blobKeyword()) {
102 m_binaryType = BinaryType::Blob;
103 return { };
104 }
105 if (binaryType == arraybufferKeyword()) {
106 m_binaryType = BinaryType::ArrayBuffer;
107 return { };
108 }
109 return Exception { TypeMismatchError };
110}
111
112ExceptionOr<void> RTCDataChannel::send(const String& data)
113{
114 if (m_readyState != RTCDataChannelState::Open)
115 return Exception { InvalidStateError };
116
117 if (!m_handler->sendStringData(data)) {
118 // FIXME: Decide what the right exception here is.
119 return Exception { SyntaxError };
120 }
121
122 return { };
123}
124
125ExceptionOr<void> RTCDataChannel::sendRawData(const char* data, size_t length)
126{
127 if (m_readyState != RTCDataChannelState::Open)
128 return Exception { InvalidStateError };
129
130 if (!length)
131 return { };
132
133 if (!m_handler->sendRawData(data, length)) {
134 // FIXME: Decide what the right exception here is.
135 return Exception { SyntaxError };
136 }
137
138 return { };
139}
140
141
142ExceptionOr<void> RTCDataChannel::send(ArrayBuffer& data)
143{
144 return sendRawData(static_cast<const char*>(data.data()), data.byteLength());
145}
146
147ExceptionOr<void> RTCDataChannel::send(ArrayBufferView& data)
148{
149 return sendRawData(static_cast<const char*>(data.baseAddress()), data.byteLength());
150}
151
152ExceptionOr<void> RTCDataChannel::send(Blob&)
153{
154 // FIXME: Implement.
155 return Exception { NotSupportedError };
156}
157
158void RTCDataChannel::close()
159{
160 if (m_stopped)
161 return;
162
163 m_stopped = true;
164 m_readyState = RTCDataChannelState::Closed;
165
166 m_handler->close();
167 m_handler = nullptr;
168 unsetPendingActivity(*this);
169}
170
171void RTCDataChannel::didChangeReadyState(RTCDataChannelState newState)
172{
173 if (m_stopped || m_readyState == RTCDataChannelState::Closed || m_readyState == newState)
174 return;
175
176 m_readyState = newState;
177
178 switch (m_readyState) {
179 case RTCDataChannelState::Open:
180 scheduleDispatchEvent(Event::create(eventNames().openEvent, Event::CanBubble::No, Event::IsCancelable::No));
181 break;
182 case RTCDataChannelState::Closed:
183 scheduleDispatchEvent(Event::create(eventNames().closeEvent, Event::CanBubble::No, Event::IsCancelable::No));
184 break;
185 default:
186 break;
187 }
188}
189
190void RTCDataChannel::didReceiveStringData(const String& text)
191{
192 if (m_stopped)
193 return;
194
195 scheduleDispatchEvent(MessageEvent::create(text));
196}
197
198void RTCDataChannel::didReceiveRawData(const char* data, size_t dataLength)
199{
200 if (m_stopped)
201 return;
202
203 switch (m_binaryType) {
204 case BinaryType::Blob:
205 scheduleDispatchEvent(MessageEvent::create(Blob::create(SharedBuffer::create(data, dataLength), emptyString()), { }));
206 return;
207 case BinaryType::ArrayBuffer:
208 scheduleDispatchEvent(MessageEvent::create(ArrayBuffer::create(data, dataLength)));
209 return;
210 }
211 ASSERT_NOT_REACHED();
212}
213
214void RTCDataChannel::didDetectError()
215{
216 if (m_stopped)
217 return;
218
219 scheduleDispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
220}
221
222void RTCDataChannel::bufferedAmountIsDecreasing(size_t amount)
223{
224 if (m_stopped)
225 return;
226
227 if (amount <= m_bufferedAmountLowThreshold)
228 scheduleDispatchEvent(Event::create(eventNames().bufferedamountlowEvent, Event::CanBubble::No, Event::IsCancelable::No));
229}
230
231void RTCDataChannel::stop()
232{
233 close();
234}
235
236void RTCDataChannel::scheduleDispatchEvent(Ref<Event>&& event)
237{
238 m_scheduledEvents.append(WTFMove(event));
239
240 if (!m_scheduledEventTimer.isActive())
241 m_scheduledEventTimer.startOneShot(0_s);
242}
243
244void RTCDataChannel::scheduledEventTimerFired()
245{
246 if (m_stopped)
247 return;
248
249 Vector<Ref<Event>> events;
250 events.swap(m_scheduledEvents);
251
252 for (auto& event : events)
253 dispatchEvent(event);
254}
255
256} // namespace WebCore
257
258#endif // ENABLE(WEB_RTC)
259