Bumping manifests a=b2g-bump
[gecko.git] / dom / base / nsDOMDataChannel.cpp
blob661217d335cd1230232b5ce307a27beb42da0f64
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsDOMDataChannel.h"
9 #include "base/basictypes.h"
10 #include "prlog.h"
12 #ifdef PR_LOGGING
13 extern PRLogModuleInfo* GetDataChannelLog();
14 #endif
15 #undef LOG
16 #define LOG(args) PR_LOG(GetDataChannelLog(), PR_LOG_DEBUG, args)
19 #include "nsDOMDataChannelDeclarations.h"
20 #include "nsDOMDataChannel.h"
21 #include "nsIDOMDataChannel.h"
22 #include "nsIDOMMessageEvent.h"
23 #include "mozilla/DOMEventTargetHelper.h"
24 #include "mozilla/dom/File.h"
25 #include "mozilla/dom/ScriptSettings.h"
27 #include "nsError.h"
28 #include "nsAutoPtr.h"
29 #include "nsContentUtils.h"
30 #include "nsCycleCollectionParticipant.h"
31 #include "nsIScriptObjectPrincipal.h"
32 #include "nsNetUtil.h"
34 #include "DataChannel.h"
36 // Since we've moved the windows.h include down here, we have to explicitly
37 // undef GetBinaryType, otherwise we'll get really odd conflicts
38 #ifdef GetBinaryType
39 #undef GetBinaryType
40 #endif
42 using namespace mozilla;
43 using namespace mozilla::dom;
45 nsDOMDataChannel::~nsDOMDataChannel()
47 // Don't call us anymore! Likely isn't an issue (or maybe just less of
48 // one) once we block GC until all the (appropriate) onXxxx handlers
49 // are dropped. (See WebRTC spec)
50 LOG(("Close()ing %p", mDataChannel.get()));
51 mDataChannel->SetListener(nullptr, nullptr);
52 mDataChannel->Close();
55 /* virtual */ JSObject*
56 nsDOMDataChannel::WrapObject(JSContext* aCx)
58 return DataChannelBinding::Wrap(aCx, this);
61 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMDataChannel)
63 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDataChannel,
64 DOMEventTargetHelper)
65 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
67 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDataChannel,
68 DOMEventTargetHelper)
69 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
71 NS_IMPL_ADDREF_INHERITED(nsDOMDataChannel, DOMEventTargetHelper)
72 NS_IMPL_RELEASE_INHERITED(nsDOMDataChannel, DOMEventTargetHelper)
74 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDataChannel)
75 NS_INTERFACE_MAP_ENTRY(nsIDOMDataChannel)
76 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
78 nsDOMDataChannel::nsDOMDataChannel(already_AddRefed<mozilla::DataChannel>& aDataChannel,
79 nsPIDOMWindow* aWindow)
80 : DOMEventTargetHelper(aWindow && aWindow->IsOuterWindow() ?
81 aWindow->GetCurrentInnerWindow() : aWindow)
82 , mDataChannel(aDataChannel)
83 , mBinaryType(DC_BINARY_TYPE_BLOB)
87 nsresult
88 nsDOMDataChannel::Init(nsPIDOMWindow* aDOMWindow)
90 nsresult rv;
91 nsAutoString urlParam;
93 MOZ_ASSERT(mDataChannel);
94 mDataChannel->SetListener(this, nullptr);
96 // Now grovel through the objects to get a usable origin for onMessage
97 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aDOMWindow);
98 NS_ENSURE_STATE(sgo);
99 nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
100 NS_ENSURE_STATE(scriptContext);
102 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(do_QueryInterface(aDOMWindow));
103 NS_ENSURE_STATE(scriptPrincipal);
104 nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
105 NS_ENSURE_STATE(principal);
107 // Attempt to kill "ghost" DataChannel (if one can happen): but usually too early for check to fail
108 rv = CheckInnerWindowCorrectness();
109 NS_ENSURE_SUCCESS(rv,rv);
111 rv = nsContentUtils::GetUTFOrigin(principal,mOrigin);
112 LOG(("%s: origin = %s\n",__FUNCTION__,NS_LossyConvertUTF16toASCII(mOrigin).get()));
113 return rv;
116 NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, open)
117 NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, error)
118 NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, close)
119 NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, message)
121 NS_IMETHODIMP
122 nsDOMDataChannel::GetLabel(nsAString& aLabel)
124 mDataChannel->GetLabel(aLabel);
125 return NS_OK;
128 NS_IMETHODIMP
129 nsDOMDataChannel::GetProtocol(nsAString& aProtocol)
131 mDataChannel->GetProtocol(aProtocol);
132 return NS_OK;
135 uint16_t
136 nsDOMDataChannel::Id() const
138 return mDataChannel->GetStream();
141 NS_IMETHODIMP
142 nsDOMDataChannel::GetId(uint16_t *aId)
144 *aId = Id();
145 return NS_OK;
148 uint16_t
149 nsDOMDataChannel::Stream() const
151 return mDataChannel->GetStream();
154 NS_IMETHODIMP
155 nsDOMDataChannel::GetStream(uint16_t *aStream)
157 *aStream = Stream();
158 return NS_OK;
161 // XXX should be GetType()? Open question for the spec
162 bool
163 nsDOMDataChannel::Reliable() const
165 return mDataChannel->GetType() == mozilla::DataChannelConnection::RELIABLE;
168 NS_IMETHODIMP
169 nsDOMDataChannel::GetReliable(bool* aReliable)
171 *aReliable = Reliable();
172 return NS_OK;
175 bool
176 nsDOMDataChannel::Ordered() const
178 return mDataChannel->GetOrdered();
181 NS_IMETHODIMP
182 nsDOMDataChannel::GetOrdered(bool* aOrdered)
184 *aOrdered = Ordered();
185 return NS_OK;
188 RTCDataChannelState
189 nsDOMDataChannel::ReadyState() const
191 return static_cast<RTCDataChannelState>(mDataChannel->GetReadyState());
195 NS_IMETHODIMP
196 nsDOMDataChannel::GetReadyState(nsAString& aReadyState)
198 uint16_t readyState = mDataChannel->GetReadyState();
199 // From the WebRTC spec
200 const char * stateName[] = {
201 "connecting",
202 "open",
203 "closing",
204 "closed"
206 MOZ_ASSERT(/*readyState >= mozilla::DataChannel::CONNECTING && */ // Always true due to datatypes
207 readyState <= mozilla::DataChannel::CLOSED);
208 aReadyState.AssignASCII(stateName[readyState]);
210 return NS_OK;
213 uint32_t
214 nsDOMDataChannel::BufferedAmount() const
216 return mDataChannel->GetBufferedAmount();
219 NS_IMETHODIMP
220 nsDOMDataChannel::GetBufferedAmount(uint32_t* aBufferedAmount)
222 *aBufferedAmount = BufferedAmount();
223 return NS_OK;
226 NS_IMETHODIMP nsDOMDataChannel::GetBinaryType(nsAString & aBinaryType)
228 switch (mBinaryType) {
229 case DC_BINARY_TYPE_ARRAYBUFFER:
230 aBinaryType.AssignLiteral("arraybuffer");
231 break;
232 case DC_BINARY_TYPE_BLOB:
233 aBinaryType.AssignLiteral("blob");
234 break;
235 default:
236 NS_ERROR("Should not happen");
238 return NS_OK;
241 NS_IMETHODIMP
242 nsDOMDataChannel::SetBinaryType(const nsAString& aBinaryType)
244 if (aBinaryType.EqualsLiteral("arraybuffer")) {
245 mBinaryType = DC_BINARY_TYPE_ARRAYBUFFER;
246 } else if (aBinaryType.EqualsLiteral("blob")) {
247 mBinaryType = DC_BINARY_TYPE_BLOB;
248 } else {
249 return NS_ERROR_INVALID_ARG;
251 return NS_OK;
254 NS_IMETHODIMP
255 nsDOMDataChannel::Close()
257 mDataChannel->Close();
258 return NS_OK;
261 // All of the following is copy/pasted from WebSocket.cpp.
262 void
263 nsDOMDataChannel::Send(const nsAString& aData, ErrorResult& aRv)
265 NS_ConvertUTF16toUTF8 msgString(aData);
266 Send(nullptr, msgString, msgString.Length(), false, aRv);
269 void
270 nsDOMDataChannel::Send(File& aData, ErrorResult& aRv)
272 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
274 nsCOMPtr<nsIInputStream> msgStream;
275 nsresult rv = aData.GetInternalStream(getter_AddRefs(msgStream));
276 if (NS_FAILED(rv)) {
277 aRv.Throw(rv);
278 return;
281 uint64_t msgLength;
282 rv = aData.GetSize(&msgLength);
283 if (NS_FAILED(rv)) {
284 aRv.Throw(rv);
285 return;
288 if (msgLength > UINT32_MAX) {
289 aRv.Throw(NS_ERROR_FILE_TOO_BIG);
290 return;
293 Send(msgStream, EmptyCString(), msgLength, true, aRv);
296 void
297 nsDOMDataChannel::Send(const ArrayBuffer& aData, ErrorResult& aRv)
299 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
301 aData.ComputeLengthAndData();
303 static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
305 uint32_t len = aData.Length();
306 char* data = reinterpret_cast<char*>(aData.Data());
308 nsDependentCSubstring msgString(data, len);
309 Send(nullptr, msgString, len, true, aRv);
312 void
313 nsDOMDataChannel::Send(const ArrayBufferView& aData, ErrorResult& aRv)
315 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
317 aData.ComputeLengthAndData();
319 static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
321 uint32_t len = aData.Length();
322 char* data = reinterpret_cast<char*>(aData.Data());
324 nsDependentCSubstring msgString(data, len);
325 Send(nullptr, msgString, len, true, aRv);
328 void
329 nsDOMDataChannel::Send(nsIInputStream* aMsgStream,
330 const nsACString& aMsgString,
331 uint32_t aMsgLength,
332 bool aIsBinary,
333 ErrorResult& aRv)
335 MOZ_ASSERT(NS_IsMainThread());
336 uint16_t state = mDataChannel->GetReadyState();
338 // In reality, the DataChannel protocol allows this, but we want it to
339 // look like WebSockets
340 if (state == mozilla::DataChannel::CONNECTING) {
341 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
342 return;
345 if (state == mozilla::DataChannel::CLOSING ||
346 state == mozilla::DataChannel::CLOSED) {
347 return;
350 MOZ_ASSERT(state == mozilla::DataChannel::OPEN,
351 "Unknown state in nsDOMDataChannel::Send");
353 int32_t sent;
354 if (aMsgStream) {
355 sent = mDataChannel->SendBinaryStream(aMsgStream, aMsgLength);
356 } else {
357 if (aIsBinary) {
358 sent = mDataChannel->SendBinaryMsg(aMsgString);
359 } else {
360 sent = mDataChannel->SendMsg(aMsgString);
363 if (sent < 0) {
364 aRv.Throw(NS_ERROR_FAILURE);
368 nsresult
369 nsDOMDataChannel::DoOnMessageAvailable(const nsACString& aData,
370 bool aBinary)
372 MOZ_ASSERT(NS_IsMainThread());
374 LOG(("DoOnMessageAvailable%s\n",aBinary ? ((mBinaryType == DC_BINARY_TYPE_BLOB) ? " (blob)" : " (binary)") : ""));
376 nsresult rv = CheckInnerWindowCorrectness();
377 if (NS_FAILED(rv)) {
378 return NS_OK;
381 AutoJSAPI jsapi;
382 if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
383 return NS_ERROR_FAILURE;
385 JSContext* cx = jsapi.cx();
387 JS::Rooted<JS::Value> jsData(cx);
389 if (aBinary) {
390 if (mBinaryType == DC_BINARY_TYPE_BLOB) {
391 rv = nsContentUtils::CreateBlobBuffer(cx, GetOwner(), aData, &jsData);
392 NS_ENSURE_SUCCESS(rv, rv);
393 } else if (mBinaryType == DC_BINARY_TYPE_ARRAYBUFFER) {
394 JS::Rooted<JSObject*> arrayBuf(cx);
395 rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address());
396 NS_ENSURE_SUCCESS(rv, rv);
397 jsData = OBJECT_TO_JSVAL(arrayBuf);
398 } else {
399 NS_RUNTIMEABORT("Unknown binary type!");
400 return NS_ERROR_UNEXPECTED;
402 } else {
403 NS_ConvertUTF8toUTF16 utf16data(aData);
404 JSString* jsString = JS_NewUCStringCopyN(cx, utf16data.get(), utf16data.Length());
405 NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
407 jsData = STRING_TO_JSVAL(jsString);
410 nsCOMPtr<nsIDOMEvent> event;
411 rv = NS_NewDOMMessageEvent(getter_AddRefs(event), this, nullptr, nullptr);
412 NS_ENSURE_SUCCESS(rv,rv);
414 nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
415 rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
416 false, false,
417 jsData, mOrigin, EmptyString(),
418 nullptr);
419 NS_ENSURE_SUCCESS(rv,rv);
420 event->SetTrusted(true);
422 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
423 rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
424 if (NS_FAILED(rv)) {
425 NS_WARNING("Failed to dispatch the message event!!!");
427 return rv;
430 nsresult
431 nsDOMDataChannel::OnMessageAvailable(nsISupports* aContext,
432 const nsACString& aMessage)
434 MOZ_ASSERT(NS_IsMainThread());
435 return DoOnMessageAvailable(aMessage, false);
438 nsresult
439 nsDOMDataChannel::OnBinaryMessageAvailable(nsISupports* aContext,
440 const nsACString& aMessage)
442 MOZ_ASSERT(NS_IsMainThread());
443 return DoOnMessageAvailable(aMessage, true);
446 nsresult
447 nsDOMDataChannel::OnSimpleEvent(nsISupports* aContext, const nsAString& aName)
449 MOZ_ASSERT(NS_IsMainThread());
451 nsresult rv = CheckInnerWindowCorrectness();
452 if (NS_FAILED(rv)) {
453 return NS_OK;
456 nsCOMPtr<nsIDOMEvent> event;
457 rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
458 NS_ENSURE_SUCCESS(rv,rv);
460 rv = event->InitEvent(aName, false, false);
461 NS_ENSURE_SUCCESS(rv,rv);
463 event->SetTrusted(true);
465 return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
468 nsresult
469 nsDOMDataChannel::OnChannelConnected(nsISupports* aContext)
471 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
473 return OnSimpleEvent(aContext, NS_LITERAL_STRING("open"));
476 nsresult
477 nsDOMDataChannel::OnChannelClosed(nsISupports* aContext)
479 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
481 return OnSimpleEvent(aContext, NS_LITERAL_STRING("close"));
484 void
485 nsDOMDataChannel::AppReady()
487 mDataChannel->AppReady();
490 /* static */
491 nsresult
492 NS_NewDOMDataChannel(already_AddRefed<mozilla::DataChannel>&& aDataChannel,
493 nsPIDOMWindow* aWindow,
494 nsIDOMDataChannel** aDomDataChannel)
496 nsRefPtr<nsDOMDataChannel> domdc =
497 new nsDOMDataChannel(aDataChannel, aWindow);
499 nsresult rv = domdc->Init(aWindow);
500 NS_ENSURE_SUCCESS(rv,rv);
502 return CallQueryInterface(domdc, aDomDataChannel);
505 /* static */
506 void
507 NS_DataChannelAppReady(nsIDOMDataChannel* aDomDataChannel)
509 ((nsDOMDataChannel *)aDomDataChannel)->AppReady();