Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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 #ifdef MOZ_LOGGING
10 #define FORCE_PR_LOG
11 #endif
13 #include "base/basictypes.h"
14 #include "prlog.h"
16 #ifdef PR_LOGGING
17 extern PRLogModuleInfo* GetDataChannelLog();
18 #endif
19 #undef LOG
20 #define LOG(args) PR_LOG(GetDataChannelLog(), PR_LOG_DEBUG, args)
23 #include "nsDOMDataChannelDeclarations.h"
24 #include "nsDOMDataChannel.h"
25 #include "nsIDOMFile.h"
26 #include "nsIDOMDataChannel.h"
27 #include "nsIDOMMessageEvent.h"
28 #include "mozilla/DOMEventTargetHelper.h"
30 #include "nsError.h"
31 #include "nsAutoPtr.h"
32 #include "nsContentUtils.h"
33 #include "nsCxPusher.h"
34 #include "nsCycleCollectionParticipant.h"
35 #include "nsIScriptObjectPrincipal.h"
36 #include "nsNetUtil.h"
37 #include "nsDOMFile.h"
39 #include "DataChannel.h"
41 // Since we've moved the windows.h include down here, we have to explicitly
42 // undef GetBinaryType, otherwise we'll get really odd conflicts
43 #ifdef GetBinaryType
44 #undef GetBinaryType
45 #endif
47 using namespace mozilla;
48 using namespace mozilla::dom;
50 nsDOMDataChannel::~nsDOMDataChannel()
51 {
52 // Don't call us anymore! Likely isn't an issue (or maybe just less of
53 // one) once we block GC until all the (appropriate) onXxxx handlers
54 // are dropped. (See WebRTC spec)
55 LOG(("Close()ing %p", mDataChannel.get()));
56 mDataChannel->SetListener(nullptr, nullptr);
57 mDataChannel->Close();
58 }
60 /* virtual */ JSObject*
61 nsDOMDataChannel::WrapObject(JSContext* aCx)
62 {
63 return DataChannelBinding::Wrap(aCx, this);
64 }
66 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMDataChannel)
68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDataChannel,
69 DOMEventTargetHelper)
70 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
72 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDataChannel,
73 DOMEventTargetHelper)
74 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
76 NS_IMPL_ADDREF_INHERITED(nsDOMDataChannel, DOMEventTargetHelper)
77 NS_IMPL_RELEASE_INHERITED(nsDOMDataChannel, DOMEventTargetHelper)
79 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDataChannel)
80 NS_INTERFACE_MAP_ENTRY(nsIDOMDataChannel)
81 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
83 nsDOMDataChannel::nsDOMDataChannel(already_AddRefed<mozilla::DataChannel>& aDataChannel,
84 nsPIDOMWindow* aWindow)
85 : DOMEventTargetHelper(aWindow && aWindow->IsOuterWindow() ?
86 aWindow->GetCurrentInnerWindow() : aWindow)
87 , mDataChannel(aDataChannel)
88 , mBinaryType(DC_BINARY_TYPE_BLOB)
89 {
90 }
92 nsresult
93 nsDOMDataChannel::Init(nsPIDOMWindow* aDOMWindow)
94 {
95 nsresult rv;
96 nsAutoString urlParam;
98 MOZ_ASSERT(mDataChannel);
99 mDataChannel->SetListener(this, nullptr);
101 // Now grovel through the objects to get a usable origin for onMessage
102 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aDOMWindow);
103 NS_ENSURE_STATE(sgo);
104 nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
105 NS_ENSURE_STATE(scriptContext);
107 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(do_QueryInterface(aDOMWindow));
108 NS_ENSURE_STATE(scriptPrincipal);
109 nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
110 NS_ENSURE_STATE(principal);
112 // Attempt to kill "ghost" DataChannel (if one can happen): but usually too early for check to fail
113 rv = CheckInnerWindowCorrectness();
114 NS_ENSURE_SUCCESS(rv,rv);
116 rv = nsContentUtils::GetUTFOrigin(principal,mOrigin);
117 LOG(("%s: origin = %s\n",__FUNCTION__,NS_LossyConvertUTF16toASCII(mOrigin).get()));
118 return rv;
119 }
121 NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, open)
122 NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, error)
123 NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, close)
124 NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, message)
126 NS_IMETHODIMP
127 nsDOMDataChannel::GetLabel(nsAString& aLabel)
128 {
129 mDataChannel->GetLabel(aLabel);
130 return NS_OK;
131 }
133 NS_IMETHODIMP
134 nsDOMDataChannel::GetProtocol(nsAString& aProtocol)
135 {
136 mDataChannel->GetProtocol(aProtocol);
137 return NS_OK;
138 }
140 uint16_t
141 nsDOMDataChannel::Id() const
142 {
143 return mDataChannel->GetStream();
144 }
146 NS_IMETHODIMP
147 nsDOMDataChannel::GetId(uint16_t *aId)
148 {
149 *aId = Id();
150 return NS_OK;
151 }
153 uint16_t
154 nsDOMDataChannel::Stream() const
155 {
156 return mDataChannel->GetStream();
157 }
159 NS_IMETHODIMP
160 nsDOMDataChannel::GetStream(uint16_t *aStream)
161 {
162 *aStream = Stream();
163 return NS_OK;
164 }
166 // XXX should be GetType()? Open question for the spec
167 bool
168 nsDOMDataChannel::Reliable() const
169 {
170 return mDataChannel->GetType() == mozilla::DataChannelConnection::RELIABLE;
171 }
173 NS_IMETHODIMP
174 nsDOMDataChannel::GetReliable(bool* aReliable)
175 {
176 *aReliable = Reliable();
177 return NS_OK;
178 }
180 bool
181 nsDOMDataChannel::Ordered() const
182 {
183 return mDataChannel->GetOrdered();
184 }
186 NS_IMETHODIMP
187 nsDOMDataChannel::GetOrdered(bool* aOrdered)
188 {
189 *aOrdered = Ordered();
190 return NS_OK;
191 }
193 RTCDataChannelState
194 nsDOMDataChannel::ReadyState() const
195 {
196 return static_cast<RTCDataChannelState>(mDataChannel->GetReadyState());
197 }
200 NS_IMETHODIMP
201 nsDOMDataChannel::GetReadyState(nsAString& aReadyState)
202 {
203 uint16_t readyState = mDataChannel->GetReadyState();
204 // From the WebRTC spec
205 const char * stateName[] = {
206 "connecting",
207 "open",
208 "closing",
209 "closed"
210 };
211 MOZ_ASSERT(/*readyState >= mozilla::DataChannel::CONNECTING && */ // Always true due to datatypes
212 readyState <= mozilla::DataChannel::CLOSED);
213 aReadyState.AssignASCII(stateName[readyState]);
215 return NS_OK;
216 }
218 uint32_t
219 nsDOMDataChannel::BufferedAmount() const
220 {
221 return mDataChannel->GetBufferedAmount();
222 }
224 NS_IMETHODIMP
225 nsDOMDataChannel::GetBufferedAmount(uint32_t* aBufferedAmount)
226 {
227 *aBufferedAmount = BufferedAmount();
228 return NS_OK;
229 }
231 NS_IMETHODIMP nsDOMDataChannel::GetBinaryType(nsAString & aBinaryType)
232 {
233 switch (mBinaryType) {
234 case DC_BINARY_TYPE_ARRAYBUFFER:
235 aBinaryType.AssignLiteral("arraybuffer");
236 break;
237 case DC_BINARY_TYPE_BLOB:
238 aBinaryType.AssignLiteral("blob");
239 break;
240 default:
241 NS_ERROR("Should not happen");
242 }
243 return NS_OK;
244 }
246 NS_IMETHODIMP
247 nsDOMDataChannel::SetBinaryType(const nsAString& aBinaryType)
248 {
249 if (aBinaryType.EqualsLiteral("arraybuffer")) {
250 mBinaryType = DC_BINARY_TYPE_ARRAYBUFFER;
251 } else if (aBinaryType.EqualsLiteral("blob")) {
252 mBinaryType = DC_BINARY_TYPE_BLOB;
253 } else {
254 return NS_ERROR_INVALID_ARG;
255 }
256 return NS_OK;
257 }
259 NS_IMETHODIMP
260 nsDOMDataChannel::Close()
261 {
262 mDataChannel->Close();
263 return NS_OK;
264 }
266 // All of the following is copy/pasted from WebSocket.cpp.
267 void
268 nsDOMDataChannel::Send(const nsAString& aData, ErrorResult& aRv)
269 {
270 NS_ConvertUTF16toUTF8 msgString(aData);
271 Send(nullptr, msgString, msgString.Length(), false, aRv);
272 }
274 void
275 nsDOMDataChannel::Send(nsIDOMBlob* aData, ErrorResult& aRv)
276 {
277 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
279 nsCOMPtr<nsIInputStream> msgStream;
280 nsresult rv = aData->GetInternalStream(getter_AddRefs(msgStream));
281 if (NS_FAILED(rv)) {
282 aRv.Throw(rv);
283 return;
284 }
286 uint64_t msgLength;
287 rv = aData->GetSize(&msgLength);
288 if (NS_FAILED(rv)) {
289 aRv.Throw(rv);
290 return;
291 }
293 if (msgLength > UINT32_MAX) {
294 aRv.Throw(NS_ERROR_FILE_TOO_BIG);
295 return;
296 }
298 Send(msgStream, EmptyCString(), msgLength, true, aRv);
299 }
301 void
302 nsDOMDataChannel::Send(const ArrayBuffer& aData, ErrorResult& aRv)
303 {
304 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
306 aData.ComputeLengthAndData();
308 static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
310 uint32_t len = aData.Length();
311 char* data = reinterpret_cast<char*>(aData.Data());
313 nsDependentCSubstring msgString(data, len);
314 Send(nullptr, msgString, len, true, aRv);
315 }
317 void
318 nsDOMDataChannel::Send(const ArrayBufferView& aData, ErrorResult& aRv)
319 {
320 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
322 aData.ComputeLengthAndData();
324 static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
326 uint32_t len = aData.Length();
327 char* data = reinterpret_cast<char*>(aData.Data());
329 nsDependentCSubstring msgString(data, len);
330 Send(nullptr, msgString, len, true, aRv);
331 }
333 void
334 nsDOMDataChannel::Send(nsIInputStream* aMsgStream,
335 const nsACString& aMsgString,
336 uint32_t aMsgLength,
337 bool aIsBinary,
338 ErrorResult& aRv)
339 {
340 MOZ_ASSERT(NS_IsMainThread());
341 uint16_t state = mDataChannel->GetReadyState();
343 // In reality, the DataChannel protocol allows this, but we want it to
344 // look like WebSockets
345 if (state == mozilla::DataChannel::CONNECTING) {
346 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
347 return;
348 }
350 if (state == mozilla::DataChannel::CLOSING ||
351 state == mozilla::DataChannel::CLOSED) {
352 return;
353 }
355 MOZ_ASSERT(state == mozilla::DataChannel::OPEN,
356 "Unknown state in nsDOMDataChannel::Send");
358 int32_t sent;
359 if (aMsgStream) {
360 sent = mDataChannel->SendBinaryStream(aMsgStream, aMsgLength);
361 } else {
362 if (aIsBinary) {
363 sent = mDataChannel->SendBinaryMsg(aMsgString);
364 } else {
365 sent = mDataChannel->SendMsg(aMsgString);
366 }
367 }
368 if (sent < 0) {
369 aRv.Throw(NS_ERROR_FAILURE);
370 }
371 }
373 nsresult
374 nsDOMDataChannel::DoOnMessageAvailable(const nsACString& aData,
375 bool aBinary)
376 {
377 MOZ_ASSERT(NS_IsMainThread());
379 LOG(("DoOnMessageAvailable%s\n",aBinary ? ((mBinaryType == DC_BINARY_TYPE_BLOB) ? " (blob)" : " (binary)") : ""));
381 nsresult rv = CheckInnerWindowCorrectness();
382 if (NS_FAILED(rv)) {
383 return NS_OK;
384 }
385 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner());
386 NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
388 nsIScriptContext* sc = sgo->GetContext();
389 NS_ENSURE_TRUE(sc, NS_ERROR_FAILURE);
391 AutoPushJSContext cx(sc->GetNativeContext());
392 NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
394 JS::Rooted<JS::Value> jsData(cx);
396 if (aBinary) {
397 if (mBinaryType == DC_BINARY_TYPE_BLOB) {
398 rv = nsContentUtils::CreateBlobBuffer(cx, aData, &jsData);
399 NS_ENSURE_SUCCESS(rv, rv);
400 } else if (mBinaryType == DC_BINARY_TYPE_ARRAYBUFFER) {
401 JS::Rooted<JSObject*> arrayBuf(cx);
402 rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address());
403 NS_ENSURE_SUCCESS(rv, rv);
404 jsData = OBJECT_TO_JSVAL(arrayBuf);
405 } else {
406 NS_RUNTIMEABORT("Unknown binary type!");
407 return NS_ERROR_UNEXPECTED;
408 }
409 } else {
410 NS_ConvertUTF8toUTF16 utf16data(aData);
411 JSString* jsString = JS_NewUCStringCopyN(cx, utf16data.get(), utf16data.Length());
412 NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
414 jsData = STRING_TO_JSVAL(jsString);
415 }
417 nsCOMPtr<nsIDOMEvent> event;
418 rv = NS_NewDOMMessageEvent(getter_AddRefs(event), this, nullptr, nullptr);
419 NS_ENSURE_SUCCESS(rv,rv);
421 nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
422 rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
423 false, false,
424 jsData, mOrigin, EmptyString(),
425 nullptr);
426 NS_ENSURE_SUCCESS(rv,rv);
427 event->SetTrusted(true);
429 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
430 rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
431 if (NS_FAILED(rv)) {
432 NS_WARNING("Failed to dispatch the message event!!!");
433 }
434 return rv;
435 }
437 nsresult
438 nsDOMDataChannel::OnMessageAvailable(nsISupports* aContext,
439 const nsACString& aMessage)
440 {
441 MOZ_ASSERT(NS_IsMainThread());
442 return DoOnMessageAvailable(aMessage, false);
443 }
445 nsresult
446 nsDOMDataChannel::OnBinaryMessageAvailable(nsISupports* aContext,
447 const nsACString& aMessage)
448 {
449 MOZ_ASSERT(NS_IsMainThread());
450 return DoOnMessageAvailable(aMessage, true);
451 }
453 nsresult
454 nsDOMDataChannel::OnSimpleEvent(nsISupports* aContext, const nsAString& aName)
455 {
456 MOZ_ASSERT(NS_IsMainThread());
458 nsresult rv = CheckInnerWindowCorrectness();
459 if (NS_FAILED(rv)) {
460 return NS_OK;
461 }
463 nsCOMPtr<nsIDOMEvent> event;
464 rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
465 NS_ENSURE_SUCCESS(rv,rv);
467 rv = event->InitEvent(aName, false, false);
468 NS_ENSURE_SUCCESS(rv,rv);
470 event->SetTrusted(true);
472 return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
473 }
475 nsresult
476 nsDOMDataChannel::OnChannelConnected(nsISupports* aContext)
477 {
478 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
480 return OnSimpleEvent(aContext, NS_LITERAL_STRING("open"));
481 }
483 nsresult
484 nsDOMDataChannel::OnChannelClosed(nsISupports* aContext)
485 {
486 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
488 return OnSimpleEvent(aContext, NS_LITERAL_STRING("close"));
489 }
491 void
492 nsDOMDataChannel::AppReady()
493 {
494 mDataChannel->AppReady();
495 }
497 /* static */
498 nsresult
499 NS_NewDOMDataChannel(already_AddRefed<mozilla::DataChannel>&& aDataChannel,
500 nsPIDOMWindow* aWindow,
501 nsIDOMDataChannel** aDomDataChannel)
502 {
503 nsRefPtr<nsDOMDataChannel> domdc =
504 new nsDOMDataChannel(aDataChannel, aWindow);
506 nsresult rv = domdc->Init(aWindow);
507 NS_ENSURE_SUCCESS(rv,rv);
509 return CallQueryInterface(domdc, aDomDataChannel);
510 }
512 /* static */
513 void
514 NS_DataChannelAppReady(nsIDOMDataChannel* aDomDataChannel)
515 {
516 ((nsDOMDataChannel *)aDomDataChannel)->AppReady();
517 }