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"
13 extern PRLogModuleInfo
* GetDataChannelLog();
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"
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
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
,
65 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
67 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDataChannel
,
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
)
88 nsDOMDataChannel::Init(nsPIDOMWindow
* aDOMWindow
)
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
);
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()));
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
)
122 nsDOMDataChannel::GetLabel(nsAString
& aLabel
)
124 mDataChannel
->GetLabel(aLabel
);
129 nsDOMDataChannel::GetProtocol(nsAString
& aProtocol
)
131 mDataChannel
->GetProtocol(aProtocol
);
136 nsDOMDataChannel::Id() const
138 return mDataChannel
->GetStream();
142 nsDOMDataChannel::GetId(uint16_t *aId
)
149 nsDOMDataChannel::Stream() const
151 return mDataChannel
->GetStream();
155 nsDOMDataChannel::GetStream(uint16_t *aStream
)
161 // XXX should be GetType()? Open question for the spec
163 nsDOMDataChannel::Reliable() const
165 return mDataChannel
->GetType() == mozilla::DataChannelConnection::RELIABLE
;
169 nsDOMDataChannel::GetReliable(bool* aReliable
)
171 *aReliable
= Reliable();
176 nsDOMDataChannel::Ordered() const
178 return mDataChannel
->GetOrdered();
182 nsDOMDataChannel::GetOrdered(bool* aOrdered
)
184 *aOrdered
= Ordered();
189 nsDOMDataChannel::ReadyState() const
191 return static_cast<RTCDataChannelState
>(mDataChannel
->GetReadyState());
196 nsDOMDataChannel::GetReadyState(nsAString
& aReadyState
)
198 uint16_t readyState
= mDataChannel
->GetReadyState();
199 // From the WebRTC spec
200 const char * stateName
[] = {
206 MOZ_ASSERT(/*readyState >= mozilla::DataChannel::CONNECTING && */ // Always true due to datatypes
207 readyState
<= mozilla::DataChannel::CLOSED
);
208 aReadyState
.AssignASCII(stateName
[readyState
]);
214 nsDOMDataChannel::BufferedAmount() const
216 return mDataChannel
->GetBufferedAmount();
220 nsDOMDataChannel::GetBufferedAmount(uint32_t* aBufferedAmount
)
222 *aBufferedAmount
= BufferedAmount();
226 NS_IMETHODIMP
nsDOMDataChannel::GetBinaryType(nsAString
& aBinaryType
)
228 switch (mBinaryType
) {
229 case DC_BINARY_TYPE_ARRAYBUFFER
:
230 aBinaryType
.AssignLiteral("arraybuffer");
232 case DC_BINARY_TYPE_BLOB
:
233 aBinaryType
.AssignLiteral("blob");
236 NS_ERROR("Should not happen");
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
;
249 return NS_ERROR_INVALID_ARG
;
255 nsDOMDataChannel::Close()
257 mDataChannel
->Close();
261 // All of the following is copy/pasted from WebSocket.cpp.
263 nsDOMDataChannel::Send(const nsAString
& aData
, ErrorResult
& aRv
)
265 NS_ConvertUTF16toUTF8
msgString(aData
);
266 Send(nullptr, msgString
, msgString
.Length(), false, aRv
);
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
));
282 rv
= aData
.GetSize(&msgLength
);
288 if (msgLength
> UINT32_MAX
) {
289 aRv
.Throw(NS_ERROR_FILE_TOO_BIG
);
293 Send(msgStream
, EmptyCString(), msgLength
, true, aRv
);
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
);
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
);
329 nsDOMDataChannel::Send(nsIInputStream
* aMsgStream
,
330 const nsACString
& aMsgString
,
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
);
345 if (state
== mozilla::DataChannel::CLOSING
||
346 state
== mozilla::DataChannel::CLOSED
) {
350 MOZ_ASSERT(state
== mozilla::DataChannel::OPEN
,
351 "Unknown state in nsDOMDataChannel::Send");
355 sent
= mDataChannel
->SendBinaryStream(aMsgStream
, aMsgLength
);
358 sent
= mDataChannel
->SendBinaryMsg(aMsgString
);
360 sent
= mDataChannel
->SendMsg(aMsgString
);
364 aRv
.Throw(NS_ERROR_FAILURE
);
369 nsDOMDataChannel::DoOnMessageAvailable(const nsACString
& aData
,
372 MOZ_ASSERT(NS_IsMainThread());
374 LOG(("DoOnMessageAvailable%s\n",aBinary
? ((mBinaryType
== DC_BINARY_TYPE_BLOB
) ? " (blob)" : " (binary)") : ""));
376 nsresult rv
= CheckInnerWindowCorrectness();
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
);
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
);
399 NS_RUNTIMEABORT("Unknown binary type!");
400 return NS_ERROR_UNEXPECTED
;
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"),
417 jsData
, mOrigin
, EmptyString(),
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);
425 NS_WARNING("Failed to dispatch the message event!!!");
431 nsDOMDataChannel::OnMessageAvailable(nsISupports
* aContext
,
432 const nsACString
& aMessage
)
434 MOZ_ASSERT(NS_IsMainThread());
435 return DoOnMessageAvailable(aMessage
, false);
439 nsDOMDataChannel::OnBinaryMessageAvailable(nsISupports
* aContext
,
440 const nsACString
& aMessage
)
442 MOZ_ASSERT(NS_IsMainThread());
443 return DoOnMessageAvailable(aMessage
, true);
447 nsDOMDataChannel::OnSimpleEvent(nsISupports
* aContext
, const nsAString
& aName
)
449 MOZ_ASSERT(NS_IsMainThread());
451 nsresult rv
= CheckInnerWindowCorrectness();
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);
469 nsDOMDataChannel::OnChannelConnected(nsISupports
* aContext
)
471 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel
,__FUNCTION__
));
473 return OnSimpleEvent(aContext
, NS_LITERAL_STRING("open"));
477 nsDOMDataChannel::OnChannelClosed(nsISupports
* aContext
)
479 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel
,__FUNCTION__
));
481 return OnSimpleEvent(aContext
, NS_LITERAL_STRING("close"));
485 nsDOMDataChannel::AppReady()
487 mDataChannel
->AppReady();
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
);
507 NS_DataChannelAppReady(nsIDOMDataChannel
* aDomDataChannel
)
509 ((nsDOMDataChannel
*)aDomDataChannel
)->AppReady();