|
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/. */ |
|
6 |
|
7 #include "nsDOMDataChannel.h" |
|
8 |
|
9 #ifdef MOZ_LOGGING |
|
10 #define FORCE_PR_LOG |
|
11 #endif |
|
12 |
|
13 #include "base/basictypes.h" |
|
14 #include "prlog.h" |
|
15 |
|
16 #ifdef PR_LOGGING |
|
17 extern PRLogModuleInfo* GetDataChannelLog(); |
|
18 #endif |
|
19 #undef LOG |
|
20 #define LOG(args) PR_LOG(GetDataChannelLog(), PR_LOG_DEBUG, args) |
|
21 |
|
22 |
|
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" |
|
29 |
|
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" |
|
38 |
|
39 #include "DataChannel.h" |
|
40 |
|
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 |
|
46 |
|
47 using namespace mozilla; |
|
48 using namespace mozilla::dom; |
|
49 |
|
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 } |
|
59 |
|
60 /* virtual */ JSObject* |
|
61 nsDOMDataChannel::WrapObject(JSContext* aCx) |
|
62 { |
|
63 return DataChannelBinding::Wrap(aCx, this); |
|
64 } |
|
65 |
|
66 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMDataChannel) |
|
67 |
|
68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDataChannel, |
|
69 DOMEventTargetHelper) |
|
70 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
71 |
|
72 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDataChannel, |
|
73 DOMEventTargetHelper) |
|
74 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
75 |
|
76 NS_IMPL_ADDREF_INHERITED(nsDOMDataChannel, DOMEventTargetHelper) |
|
77 NS_IMPL_RELEASE_INHERITED(nsDOMDataChannel, DOMEventTargetHelper) |
|
78 |
|
79 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDataChannel) |
|
80 NS_INTERFACE_MAP_ENTRY(nsIDOMDataChannel) |
|
81 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) |
|
82 |
|
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 } |
|
91 |
|
92 nsresult |
|
93 nsDOMDataChannel::Init(nsPIDOMWindow* aDOMWindow) |
|
94 { |
|
95 nsresult rv; |
|
96 nsAutoString urlParam; |
|
97 |
|
98 MOZ_ASSERT(mDataChannel); |
|
99 mDataChannel->SetListener(this, nullptr); |
|
100 |
|
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); |
|
106 |
|
107 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(do_QueryInterface(aDOMWindow)); |
|
108 NS_ENSURE_STATE(scriptPrincipal); |
|
109 nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal(); |
|
110 NS_ENSURE_STATE(principal); |
|
111 |
|
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); |
|
115 |
|
116 rv = nsContentUtils::GetUTFOrigin(principal,mOrigin); |
|
117 LOG(("%s: origin = %s\n",__FUNCTION__,NS_LossyConvertUTF16toASCII(mOrigin).get())); |
|
118 return rv; |
|
119 } |
|
120 |
|
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) |
|
125 |
|
126 NS_IMETHODIMP |
|
127 nsDOMDataChannel::GetLabel(nsAString& aLabel) |
|
128 { |
|
129 mDataChannel->GetLabel(aLabel); |
|
130 return NS_OK; |
|
131 } |
|
132 |
|
133 NS_IMETHODIMP |
|
134 nsDOMDataChannel::GetProtocol(nsAString& aProtocol) |
|
135 { |
|
136 mDataChannel->GetProtocol(aProtocol); |
|
137 return NS_OK; |
|
138 } |
|
139 |
|
140 uint16_t |
|
141 nsDOMDataChannel::Id() const |
|
142 { |
|
143 return mDataChannel->GetStream(); |
|
144 } |
|
145 |
|
146 NS_IMETHODIMP |
|
147 nsDOMDataChannel::GetId(uint16_t *aId) |
|
148 { |
|
149 *aId = Id(); |
|
150 return NS_OK; |
|
151 } |
|
152 |
|
153 uint16_t |
|
154 nsDOMDataChannel::Stream() const |
|
155 { |
|
156 return mDataChannel->GetStream(); |
|
157 } |
|
158 |
|
159 NS_IMETHODIMP |
|
160 nsDOMDataChannel::GetStream(uint16_t *aStream) |
|
161 { |
|
162 *aStream = Stream(); |
|
163 return NS_OK; |
|
164 } |
|
165 |
|
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 } |
|
172 |
|
173 NS_IMETHODIMP |
|
174 nsDOMDataChannel::GetReliable(bool* aReliable) |
|
175 { |
|
176 *aReliable = Reliable(); |
|
177 return NS_OK; |
|
178 } |
|
179 |
|
180 bool |
|
181 nsDOMDataChannel::Ordered() const |
|
182 { |
|
183 return mDataChannel->GetOrdered(); |
|
184 } |
|
185 |
|
186 NS_IMETHODIMP |
|
187 nsDOMDataChannel::GetOrdered(bool* aOrdered) |
|
188 { |
|
189 *aOrdered = Ordered(); |
|
190 return NS_OK; |
|
191 } |
|
192 |
|
193 RTCDataChannelState |
|
194 nsDOMDataChannel::ReadyState() const |
|
195 { |
|
196 return static_cast<RTCDataChannelState>(mDataChannel->GetReadyState()); |
|
197 } |
|
198 |
|
199 |
|
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]); |
|
214 |
|
215 return NS_OK; |
|
216 } |
|
217 |
|
218 uint32_t |
|
219 nsDOMDataChannel::BufferedAmount() const |
|
220 { |
|
221 return mDataChannel->GetBufferedAmount(); |
|
222 } |
|
223 |
|
224 NS_IMETHODIMP |
|
225 nsDOMDataChannel::GetBufferedAmount(uint32_t* aBufferedAmount) |
|
226 { |
|
227 *aBufferedAmount = BufferedAmount(); |
|
228 return NS_OK; |
|
229 } |
|
230 |
|
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 } |
|
245 |
|
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 } |
|
258 |
|
259 NS_IMETHODIMP |
|
260 nsDOMDataChannel::Close() |
|
261 { |
|
262 mDataChannel->Close(); |
|
263 return NS_OK; |
|
264 } |
|
265 |
|
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 } |
|
273 |
|
274 void |
|
275 nsDOMDataChannel::Send(nsIDOMBlob* aData, ErrorResult& aRv) |
|
276 { |
|
277 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); |
|
278 |
|
279 nsCOMPtr<nsIInputStream> msgStream; |
|
280 nsresult rv = aData->GetInternalStream(getter_AddRefs(msgStream)); |
|
281 if (NS_FAILED(rv)) { |
|
282 aRv.Throw(rv); |
|
283 return; |
|
284 } |
|
285 |
|
286 uint64_t msgLength; |
|
287 rv = aData->GetSize(&msgLength); |
|
288 if (NS_FAILED(rv)) { |
|
289 aRv.Throw(rv); |
|
290 return; |
|
291 } |
|
292 |
|
293 if (msgLength > UINT32_MAX) { |
|
294 aRv.Throw(NS_ERROR_FILE_TOO_BIG); |
|
295 return; |
|
296 } |
|
297 |
|
298 Send(msgStream, EmptyCString(), msgLength, true, aRv); |
|
299 } |
|
300 |
|
301 void |
|
302 nsDOMDataChannel::Send(const ArrayBuffer& aData, ErrorResult& aRv) |
|
303 { |
|
304 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); |
|
305 |
|
306 aData.ComputeLengthAndData(); |
|
307 |
|
308 static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); |
|
309 |
|
310 uint32_t len = aData.Length(); |
|
311 char* data = reinterpret_cast<char*>(aData.Data()); |
|
312 |
|
313 nsDependentCSubstring msgString(data, len); |
|
314 Send(nullptr, msgString, len, true, aRv); |
|
315 } |
|
316 |
|
317 void |
|
318 nsDOMDataChannel::Send(const ArrayBufferView& aData, ErrorResult& aRv) |
|
319 { |
|
320 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); |
|
321 |
|
322 aData.ComputeLengthAndData(); |
|
323 |
|
324 static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); |
|
325 |
|
326 uint32_t len = aData.Length(); |
|
327 char* data = reinterpret_cast<char*>(aData.Data()); |
|
328 |
|
329 nsDependentCSubstring msgString(data, len); |
|
330 Send(nullptr, msgString, len, true, aRv); |
|
331 } |
|
332 |
|
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(); |
|
342 |
|
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 } |
|
349 |
|
350 if (state == mozilla::DataChannel::CLOSING || |
|
351 state == mozilla::DataChannel::CLOSED) { |
|
352 return; |
|
353 } |
|
354 |
|
355 MOZ_ASSERT(state == mozilla::DataChannel::OPEN, |
|
356 "Unknown state in nsDOMDataChannel::Send"); |
|
357 |
|
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 } |
|
372 |
|
373 nsresult |
|
374 nsDOMDataChannel::DoOnMessageAvailable(const nsACString& aData, |
|
375 bool aBinary) |
|
376 { |
|
377 MOZ_ASSERT(NS_IsMainThread()); |
|
378 |
|
379 LOG(("DoOnMessageAvailable%s\n",aBinary ? ((mBinaryType == DC_BINARY_TYPE_BLOB) ? " (blob)" : " (binary)") : "")); |
|
380 |
|
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); |
|
387 |
|
388 nsIScriptContext* sc = sgo->GetContext(); |
|
389 NS_ENSURE_TRUE(sc, NS_ERROR_FAILURE); |
|
390 |
|
391 AutoPushJSContext cx(sc->GetNativeContext()); |
|
392 NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); |
|
393 |
|
394 JS::Rooted<JS::Value> jsData(cx); |
|
395 |
|
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); |
|
413 |
|
414 jsData = STRING_TO_JSVAL(jsString); |
|
415 } |
|
416 |
|
417 nsCOMPtr<nsIDOMEvent> event; |
|
418 rv = NS_NewDOMMessageEvent(getter_AddRefs(event), this, nullptr, nullptr); |
|
419 NS_ENSURE_SUCCESS(rv,rv); |
|
420 |
|
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); |
|
428 |
|
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 } |
|
436 |
|
437 nsresult |
|
438 nsDOMDataChannel::OnMessageAvailable(nsISupports* aContext, |
|
439 const nsACString& aMessage) |
|
440 { |
|
441 MOZ_ASSERT(NS_IsMainThread()); |
|
442 return DoOnMessageAvailable(aMessage, false); |
|
443 } |
|
444 |
|
445 nsresult |
|
446 nsDOMDataChannel::OnBinaryMessageAvailable(nsISupports* aContext, |
|
447 const nsACString& aMessage) |
|
448 { |
|
449 MOZ_ASSERT(NS_IsMainThread()); |
|
450 return DoOnMessageAvailable(aMessage, true); |
|
451 } |
|
452 |
|
453 nsresult |
|
454 nsDOMDataChannel::OnSimpleEvent(nsISupports* aContext, const nsAString& aName) |
|
455 { |
|
456 MOZ_ASSERT(NS_IsMainThread()); |
|
457 |
|
458 nsresult rv = CheckInnerWindowCorrectness(); |
|
459 if (NS_FAILED(rv)) { |
|
460 return NS_OK; |
|
461 } |
|
462 |
|
463 nsCOMPtr<nsIDOMEvent> event; |
|
464 rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); |
|
465 NS_ENSURE_SUCCESS(rv,rv); |
|
466 |
|
467 rv = event->InitEvent(aName, false, false); |
|
468 NS_ENSURE_SUCCESS(rv,rv); |
|
469 |
|
470 event->SetTrusted(true); |
|
471 |
|
472 return DispatchDOMEvent(nullptr, event, nullptr, nullptr); |
|
473 } |
|
474 |
|
475 nsresult |
|
476 nsDOMDataChannel::OnChannelConnected(nsISupports* aContext) |
|
477 { |
|
478 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__)); |
|
479 |
|
480 return OnSimpleEvent(aContext, NS_LITERAL_STRING("open")); |
|
481 } |
|
482 |
|
483 nsresult |
|
484 nsDOMDataChannel::OnChannelClosed(nsISupports* aContext) |
|
485 { |
|
486 LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__)); |
|
487 |
|
488 return OnSimpleEvent(aContext, NS_LITERAL_STRING("close")); |
|
489 } |
|
490 |
|
491 void |
|
492 nsDOMDataChannel::AppReady() |
|
493 { |
|
494 mDataChannel->AppReady(); |
|
495 } |
|
496 |
|
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); |
|
505 |
|
506 nsresult rv = domdc->Init(aWindow); |
|
507 NS_ENSURE_SUCCESS(rv,rv); |
|
508 |
|
509 return CallQueryInterface(domdc, aDomDataChannel); |
|
510 } |
|
511 |
|
512 /* static */ |
|
513 void |
|
514 NS_DataChannelAppReady(nsIDOMDataChannel* aDomDataChannel) |
|
515 { |
|
516 ((nsDOMDataChannel *)aDomDataChannel)->AppReady(); |
|
517 } |