Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
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 "WebSocketLog.h"
8 #include "base/compiler_specific.h"
9 #include "mozilla/dom/TabChild.h"
10 #include "mozilla/net/NeckoChild.h"
11 #include "WebSocketChannelChild.h"
12 #include "nsITabChild.h"
13 #include "nsNetUtil.h"
14 #include "mozilla/ipc/InputStreamUtils.h"
15 #include "mozilla/ipc/URIUtils.h"
16 #include "mozilla/net/ChannelEventQueue.h"
17 #include "SerializedLoadContext.h"
19 using namespace mozilla::ipc;
21 namespace mozilla {
22 namespace net {
24 NS_IMPL_ADDREF(WebSocketChannelChild)
26 NS_IMETHODIMP_(MozExternalRefCountType) WebSocketChannelChild::Release()
27 {
28 NS_PRECONDITION(0 != mRefCnt, "dup release");
29 NS_ASSERT_OWNINGTHREAD(WebSocketChannelChild);
30 --mRefCnt;
31 NS_LOG_RELEASE(this, mRefCnt, "WebSocketChannelChild");
33 if (mRefCnt == 1 && mIPCOpen) {
34 SendDeleteSelf();
35 return mRefCnt;
36 }
38 if (mRefCnt == 0) {
39 mRefCnt = 1; /* stabilize */
40 delete this;
41 return 0;
42 }
43 return mRefCnt;
44 }
46 NS_INTERFACE_MAP_BEGIN(WebSocketChannelChild)
47 NS_INTERFACE_MAP_ENTRY(nsIWebSocketChannel)
48 NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler)
49 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebSocketChannel)
50 NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
51 NS_INTERFACE_MAP_END
53 WebSocketChannelChild::WebSocketChannelChild(bool aSecure)
54 : mIPCOpen(false)
55 {
56 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
58 LOG(("WebSocketChannelChild::WebSocketChannelChild() %p\n", this));
59 BaseWebSocketChannel::mEncrypted = aSecure;
60 mEventQ = new ChannelEventQueue(static_cast<nsIWebSocketChannel*>(this));
61 }
63 WebSocketChannelChild::~WebSocketChannelChild()
64 {
65 LOG(("WebSocketChannelChild::~WebSocketChannelChild() %p\n", this));
66 }
68 void
69 WebSocketChannelChild::AddIPDLReference()
70 {
71 NS_ABORT_IF_FALSE(!mIPCOpen, "Attempt to retain more than one IPDL reference");
72 mIPCOpen = true;
73 AddRef();
74 }
76 void
77 WebSocketChannelChild::ReleaseIPDLReference()
78 {
79 NS_ABORT_IF_FALSE(mIPCOpen, "Attempt to release nonexistent IPDL reference");
80 mIPCOpen = false;
81 Release();
82 }
84 class WrappedChannelEvent : public nsRunnable
85 {
86 public:
87 WrappedChannelEvent(ChannelEvent *aChannelEvent)
88 : mChannelEvent(aChannelEvent)
89 {
90 MOZ_RELEASE_ASSERT(aChannelEvent);
91 }
92 NS_IMETHOD Run()
93 {
94 mChannelEvent->Run();
95 return NS_OK;
96 }
97 private:
98 nsAutoPtr<ChannelEvent> mChannelEvent;
99 };
101 void
102 WebSocketChannelChild::DispatchToTargetThread(ChannelEvent *aChannelEvent)
103 {
104 MOZ_RELEASE_ASSERT(NS_IsMainThread());
105 MOZ_RELEASE_ASSERT(mTargetThread);
106 MOZ_RELEASE_ASSERT(aChannelEvent);
108 mTargetThread->Dispatch(new WrappedChannelEvent(aChannelEvent),
109 NS_DISPATCH_NORMAL);
110 }
112 class StartEvent : public ChannelEvent
113 {
114 public:
115 StartEvent(WebSocketChannelChild* aChild,
116 const nsCString& aProtocol,
117 const nsCString& aExtensions)
118 : mChild(aChild)
119 , mProtocol(aProtocol)
120 , mExtensions(aExtensions)
121 {}
123 void Run()
124 {
125 mChild->OnStart(mProtocol, mExtensions);
126 }
127 private:
128 WebSocketChannelChild* mChild;
129 nsCString mProtocol;
130 nsCString mExtensions;
131 };
133 bool
134 WebSocketChannelChild::RecvOnStart(const nsCString& aProtocol,
135 const nsCString& aExtensions)
136 {
137 if (mEventQ->ShouldEnqueue()) {
138 mEventQ->Enqueue(new StartEvent(this, aProtocol, aExtensions));
139 } else if (mTargetThread) {
140 DispatchToTargetThread(new StartEvent(this, aProtocol, aExtensions));
141 } else {
142 OnStart(aProtocol, aExtensions);
143 }
144 return true;
145 }
147 void
148 WebSocketChannelChild::OnStart(const nsCString& aProtocol,
149 const nsCString& aExtensions)
150 {
151 LOG(("WebSocketChannelChild::RecvOnStart() %p\n", this));
152 SetProtocol(aProtocol);
153 mNegotiatedExtensions = aExtensions;
155 if (mListener) {
156 AutoEventEnqueuer ensureSerialDispatch(mEventQ);;
157 mListener->OnStart(mContext);
158 }
159 }
161 class StopEvent : public ChannelEvent
162 {
163 public:
164 StopEvent(WebSocketChannelChild* aChild,
165 const nsresult& aStatusCode)
166 : mChild(aChild)
167 , mStatusCode(aStatusCode)
168 {}
170 void Run()
171 {
172 mChild->OnStop(mStatusCode);
173 }
174 private:
175 WebSocketChannelChild* mChild;
176 nsresult mStatusCode;
177 };
179 bool
180 WebSocketChannelChild::RecvOnStop(const nsresult& aStatusCode)
181 {
182 if (mEventQ->ShouldEnqueue()) {
183 mEventQ->Enqueue(new StopEvent(this, aStatusCode));
184 } else if (mTargetThread) {
185 DispatchToTargetThread(new StopEvent(this, aStatusCode));
186 } else {
187 OnStop(aStatusCode);
188 }
189 return true;
190 }
192 void
193 WebSocketChannelChild::OnStop(const nsresult& aStatusCode)
194 {
195 LOG(("WebSocketChannelChild::RecvOnStop() %p\n", this));
196 if (mListener) {
197 AutoEventEnqueuer ensureSerialDispatch(mEventQ);;
198 mListener->OnStop(mContext, aStatusCode);
199 }
200 }
202 class MessageEvent : public ChannelEvent
203 {
204 public:
205 MessageEvent(WebSocketChannelChild* aChild,
206 const nsCString& aMessage,
207 bool aBinary)
208 : mChild(aChild)
209 , mMessage(aMessage)
210 , mBinary(aBinary)
211 {}
213 void Run()
214 {
215 if (!mBinary) {
216 mChild->OnMessageAvailable(mMessage);
217 } else {
218 mChild->OnBinaryMessageAvailable(mMessage);
219 }
220 }
221 private:
222 WebSocketChannelChild* mChild;
223 nsCString mMessage;
224 bool mBinary;
225 };
227 bool
228 WebSocketChannelChild::RecvOnMessageAvailable(const nsCString& aMsg)
229 {
230 if (mEventQ->ShouldEnqueue()) {
231 mEventQ->Enqueue(new MessageEvent(this, aMsg, false));
232 } else if (mTargetThread) {
233 DispatchToTargetThread(new MessageEvent(this, aMsg, false));
234 } else {
235 OnMessageAvailable(aMsg);
236 }
237 return true;
238 }
240 void
241 WebSocketChannelChild::OnMessageAvailable(const nsCString& aMsg)
242 {
243 LOG(("WebSocketChannelChild::RecvOnMessageAvailable() %p\n", this));
244 if (mListener) {
245 AutoEventEnqueuer ensureSerialDispatch(mEventQ);;
246 mListener->OnMessageAvailable(mContext, aMsg);
247 }
248 }
250 bool
251 WebSocketChannelChild::RecvOnBinaryMessageAvailable(const nsCString& aMsg)
252 {
253 if (mEventQ->ShouldEnqueue()) {
254 mEventQ->Enqueue(new MessageEvent(this, aMsg, true));
255 } else if (mTargetThread) {
256 DispatchToTargetThread(new MessageEvent(this, aMsg, true));
257 } else {
258 OnBinaryMessageAvailable(aMsg);
259 }
260 return true;
261 }
263 void
264 WebSocketChannelChild::OnBinaryMessageAvailable(const nsCString& aMsg)
265 {
266 LOG(("WebSocketChannelChild::RecvOnBinaryMessageAvailable() %p\n", this));
267 if (mListener) {
268 AutoEventEnqueuer ensureSerialDispatch(mEventQ);;
269 mListener->OnBinaryMessageAvailable(mContext, aMsg);
270 }
271 }
273 class AcknowledgeEvent : public ChannelEvent
274 {
275 public:
276 AcknowledgeEvent(WebSocketChannelChild* aChild,
277 const uint32_t& aSize)
278 : mChild(aChild)
279 , mSize(aSize)
280 {}
282 void Run()
283 {
284 mChild->OnAcknowledge(mSize);
285 }
286 private:
287 WebSocketChannelChild* mChild;
288 uint32_t mSize;
289 };
291 bool
292 WebSocketChannelChild::RecvOnAcknowledge(const uint32_t& aSize)
293 {
294 if (mEventQ->ShouldEnqueue()) {
295 mEventQ->Enqueue(new AcknowledgeEvent(this, aSize));
296 } else if (mTargetThread) {
297 DispatchToTargetThread(new AcknowledgeEvent(this, aSize));
298 } else {
299 OnAcknowledge(aSize);
300 }
301 return true;
302 }
304 void
305 WebSocketChannelChild::OnAcknowledge(const uint32_t& aSize)
306 {
307 LOG(("WebSocketChannelChild::RecvOnAcknowledge() %p\n", this));
308 if (mListener) {
309 AutoEventEnqueuer ensureSerialDispatch(mEventQ);;
310 mListener->OnAcknowledge(mContext, aSize);
311 }
312 }
314 class ServerCloseEvent : public ChannelEvent
315 {
316 public:
317 ServerCloseEvent(WebSocketChannelChild* aChild,
318 const uint16_t aCode,
319 const nsCString &aReason)
320 : mChild(aChild)
321 , mCode(aCode)
322 , mReason(aReason)
323 {}
325 void Run()
326 {
327 mChild->OnServerClose(mCode, mReason);
328 }
329 private:
330 WebSocketChannelChild* mChild;
331 uint16_t mCode;
332 nsCString mReason;
333 };
335 bool
336 WebSocketChannelChild::RecvOnServerClose(const uint16_t& aCode,
337 const nsCString& aReason)
338 {
339 if (mEventQ->ShouldEnqueue()) {
340 mEventQ->Enqueue(new ServerCloseEvent(this, aCode, aReason));
341 } else if (mTargetThread) {
342 DispatchToTargetThread(new ServerCloseEvent(this, aCode, aReason));
343 } else {
344 OnServerClose(aCode, aReason);
345 }
346 return true;
347 }
349 void
350 WebSocketChannelChild::OnServerClose(const uint16_t& aCode,
351 const nsCString& aReason)
352 {
353 LOG(("WebSocketChannelChild::RecvOnServerClose() %p\n", this));
354 if (mListener) {
355 AutoEventEnqueuer ensureSerialDispatch(mEventQ);;
356 mListener->OnServerClose(mContext, aCode, aReason);
357 }
358 }
360 NS_IMETHODIMP
361 WebSocketChannelChild::AsyncOpen(nsIURI *aURI,
362 const nsACString &aOrigin,
363 nsIWebSocketListener *aListener,
364 nsISupports *aContext)
365 {
366 LOG(("WebSocketChannelChild::AsyncOpen() %p\n", this));
368 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
369 NS_ABORT_IF_FALSE(aURI && aListener && !mListener,
370 "Invalid state for WebSocketChannelChild::AsyncOpen");
372 mozilla::dom::TabChild* tabChild = nullptr;
373 nsCOMPtr<nsITabChild> iTabChild;
374 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
375 NS_GET_IID(nsITabChild),
376 getter_AddRefs(iTabChild));
377 if (iTabChild) {
378 tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
379 }
380 if (MissingRequiredTabChild(tabChild, "websocket")) {
381 return NS_ERROR_ILLEGAL_VALUE;
382 }
384 URIParams uri;
385 SerializeURI(aURI, uri);
387 // Corresponding release in DeallocPWebSocket
388 AddIPDLReference();
390 gNeckoChild->SendPWebSocketConstructor(this, tabChild,
391 IPC::SerializedLoadContext(this));
392 if (!SendAsyncOpen(uri, nsCString(aOrigin), mProtocol, mEncrypted,
393 mPingInterval, mClientSetPingInterval,
394 mPingResponseTimeout, mClientSetPingTimeout))
395 return NS_ERROR_UNEXPECTED;
397 mOriginalURI = aURI;
398 mURI = mOriginalURI;
399 mListener = aListener;
400 mContext = aContext;
401 mOrigin = aOrigin;
402 mWasOpened = 1;
404 return NS_OK;
405 }
407 class CloseEvent : public nsRunnable
408 {
409 public:
410 CloseEvent(WebSocketChannelChild *aChild,
411 uint16_t aCode,
412 const nsACString& aReason)
413 : mChild(aChild)
414 , mCode(aCode)
415 , mReason(aReason)
416 {
417 MOZ_RELEASE_ASSERT(!NS_IsMainThread());
418 MOZ_ASSERT(aChild);
419 }
420 NS_IMETHOD Run()
421 {
422 MOZ_RELEASE_ASSERT(NS_IsMainThread());
423 mChild->Close(mCode, mReason);
424 return NS_OK;
425 }
426 private:
427 nsRefPtr<WebSocketChannelChild> mChild;
428 uint16_t mCode;
429 nsCString mReason;
430 };
432 NS_IMETHODIMP
433 WebSocketChannelChild::Close(uint16_t code, const nsACString & reason)
434 {
435 if (!NS_IsMainThread()) {
436 MOZ_RELEASE_ASSERT(NS_GetCurrentThread() == mTargetThread);
437 return NS_DispatchToMainThread(new CloseEvent(this, code, reason));
438 }
439 LOG(("WebSocketChannelChild::Close() %p\n", this));
441 if (!mIPCOpen || !SendClose(code, nsCString(reason)))
442 return NS_ERROR_UNEXPECTED;
443 return NS_OK;
444 }
446 class MsgEvent : public nsRunnable
447 {
448 public:
449 MsgEvent(WebSocketChannelChild *aChild,
450 const nsACString &aMsg,
451 bool aBinaryMsg)
452 : mChild(aChild)
453 , mMsg(aMsg)
454 , mBinaryMsg(aBinaryMsg)
455 {
456 MOZ_RELEASE_ASSERT(!NS_IsMainThread());
457 MOZ_ASSERT(aChild);
458 }
459 NS_IMETHOD Run()
460 {
461 MOZ_RELEASE_ASSERT(NS_IsMainThread());
462 if (mBinaryMsg) {
463 mChild->SendBinaryMsg(mMsg);
464 } else {
465 mChild->SendMsg(mMsg);
466 }
467 return NS_OK;
468 }
469 private:
470 nsRefPtr<WebSocketChannelChild> mChild;
471 nsCString mMsg;
472 bool mBinaryMsg;
473 };
475 NS_IMETHODIMP
476 WebSocketChannelChild::SendMsg(const nsACString &aMsg)
477 {
478 if (!NS_IsMainThread()) {
479 MOZ_RELEASE_ASSERT(NS_GetCurrentThread() == mTargetThread);
480 return NS_DispatchToMainThread(new MsgEvent(this, aMsg, false));
481 }
482 LOG(("WebSocketChannelChild::SendMsg() %p\n", this));
484 if (!mIPCOpen || !SendSendMsg(nsCString(aMsg)))
485 return NS_ERROR_UNEXPECTED;
486 return NS_OK;
487 }
489 NS_IMETHODIMP
490 WebSocketChannelChild::SendBinaryMsg(const nsACString &aMsg)
491 {
492 if (!NS_IsMainThread()) {
493 MOZ_RELEASE_ASSERT(NS_GetCurrentThread() == mTargetThread);
494 return NS_DispatchToMainThread(new MsgEvent(this, aMsg, true));
495 }
496 LOG(("WebSocketChannelChild::SendBinaryMsg() %p\n", this));
498 if (!mIPCOpen || !SendSendBinaryMsg(nsCString(aMsg)))
499 return NS_ERROR_UNEXPECTED;
500 return NS_OK;
501 }
503 class BinaryStreamEvent : public nsRunnable
504 {
505 public:
506 BinaryStreamEvent(WebSocketChannelChild *aChild,
507 OptionalInputStreamParams *aStream,
508 uint32_t aLength)
509 : mChild(aChild)
510 , mStream(aStream)
511 , mLength(aLength)
512 {
513 MOZ_RELEASE_ASSERT(!NS_IsMainThread());
514 MOZ_ASSERT(aChild);
515 }
516 NS_IMETHOD Run()
517 {
518 MOZ_ASSERT(NS_IsMainThread());
519 mChild->SendBinaryStream(mStream, mLength);
520 return NS_OK;
521 }
522 private:
523 nsRefPtr<WebSocketChannelChild> mChild;
524 nsAutoPtr<OptionalInputStreamParams> mStream;
525 uint32_t mLength;
526 };
528 NS_IMETHODIMP
529 WebSocketChannelChild::SendBinaryStream(nsIInputStream *aStream,
530 uint32_t aLength)
531 {
532 OptionalInputStreamParams *stream = new OptionalInputStreamParams();
533 nsTArray<mozilla::ipc::FileDescriptor> fds;
534 SerializeInputStream(aStream, *stream, fds);
536 MOZ_ASSERT(fds.IsEmpty());
538 if (!NS_IsMainThread()) {
539 MOZ_RELEASE_ASSERT(NS_GetCurrentThread() == mTargetThread);
540 return NS_DispatchToMainThread(new BinaryStreamEvent(this, stream, aLength),
541 NS_DISPATCH_NORMAL);
542 }
543 return SendBinaryStream(stream, aLength);
544 }
546 nsresult
547 WebSocketChannelChild::SendBinaryStream(OptionalInputStreamParams *aStream,
548 uint32_t aLength)
549 {
550 LOG(("WebSocketChannelChild::SendBinaryStream() %p\n", this));
552 nsAutoPtr<OptionalInputStreamParams> stream(aStream);
554 if (!mIPCOpen || !SendSendBinaryStream(*stream, aLength))
555 return NS_ERROR_UNEXPECTED;
556 return NS_OK;
557 }
559 NS_IMETHODIMP
560 WebSocketChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo)
561 {
562 LOG(("WebSocketChannelChild::GetSecurityInfo() %p\n", this));
563 return NS_ERROR_NOT_AVAILABLE;
564 }
566 //-----------------------------------------------------------------------------
567 // WebSocketChannelChild::nsIThreadRetargetableRequest
568 //-----------------------------------------------------------------------------
570 NS_IMETHODIMP
571 WebSocketChannelChild::RetargetDeliveryTo(nsIEventTarget* aTargetThread)
572 {
573 nsresult rv = BaseWebSocketChannel::RetargetDeliveryTo(aTargetThread);
574 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
576 return mEventQ->RetargetDeliveryTo(aTargetThread);
577 }
579 } // namespace net
580 } // namespace mozilla