1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include <algorithm>
8 #include "TCPSocketChild.h"
9 #include "mozilla/HoldDropJSObjects.h"
10 #include "mozilla/Unused.h"
11 #include "mozilla/UniquePtr.h"
12 #include "mozilla/net/NeckoChild.h"
13 #include "mozilla/dom/PBrowserChild.h"
14 #include "mozilla/dom/BrowserChild.h"
15 #include "nsITCPSocketCallback.h"
16 #include "TCPSocket.h"
17 #include "nsContentUtils.h"
18 #include "js/ArrayBuffer.h"  // JS::NewArrayBufferWithContents
19 #include "js/RootingAPI.h"   // JS::MutableHandle
20 #include "js/Utility.h"  // js::ArrayBufferContentsArena, JS::FreePolicy, js_pod_arena_malloc
21 #include "js/Value.h"  // JS::Value
22 
23 using mozilla::net::gNeckoChild;
24 
25 namespace IPC {
26 
DeserializeArrayBuffer(JSContext * cx,const nsTArray<uint8_t> & aBuffer,JS::MutableHandle<JS::Value> aVal)27 bool DeserializeArrayBuffer(JSContext* cx, const nsTArray<uint8_t>& aBuffer,
28                             JS::MutableHandle<JS::Value> aVal) {
29   mozilla::UniquePtr<uint8_t[], JS::FreePolicy> data(
30       js_pod_arena_malloc<uint8_t>(js::ArrayBufferContentsArena,
31                                    aBuffer.Length()));
32   if (!data) return false;
33   memcpy(data.get(), aBuffer.Elements(), aBuffer.Length());
34 
35   JSObject* obj =
36       JS::NewArrayBufferWithContents(cx, aBuffer.Length(), data.get());
37   if (!obj) return false;
38   // If JS::NewArrayBufferWithContents returns non-null, the ownership of
39   // the data is transfered to obj, so we release the ownership here.
40   mozilla::Unused << data.release();
41 
42   aVal.setObject(*obj);
43   return true;
44 }
45 
46 }  // namespace IPC
47 
48 namespace mozilla::dom {
49 
50 NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocketChildBase)
51 
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TCPSocketChildBase)52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TCPSocketChildBase)
53   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocket)
54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
55 
56 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TCPSocketChildBase)
57   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocket)
58 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
59 
60 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(TCPSocketChildBase)
61 NS_IMPL_CYCLE_COLLECTION_TRACE_END
62 
63 NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketChildBase)
64 NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketChildBase)
65 
66 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketChildBase)
67   NS_INTERFACE_MAP_ENTRY(nsISupports)
68 NS_INTERFACE_MAP_END
69 
70 TCPSocketChildBase::TCPSocketChildBase() : mIPCOpen(false) {
71   mozilla::HoldJSObjects(this);
72 }
73 
~TCPSocketChildBase()74 TCPSocketChildBase::~TCPSocketChildBase() { mozilla::DropJSObjects(this); }
75 
NS_IMETHODIMP_(MozExternalRefCountType)76 NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketChild::Release(void) {
77   nsrefcnt refcnt = TCPSocketChildBase::Release();
78   if (refcnt == 1 && mIPCOpen) {
79     PTCPSocketChild::SendRequestDelete();
80     return 1;
81   }
82   return refcnt;
83 }
84 
TCPSocketChild(const nsAString & aHost,const uint16_t & aPort,nsISerialEventTarget * aTarget)85 TCPSocketChild::TCPSocketChild(const nsAString& aHost, const uint16_t& aPort,
86                                nsISerialEventTarget* aTarget)
87     : mHost(aHost), mPort(aPort), mIPCEventTarget(aTarget) {}
88 
SendOpen(nsITCPSocketCallback * aSocket,bool aUseSSL,bool aUseArrayBuffers)89 void TCPSocketChild::SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL,
90                               bool aUseArrayBuffers) {
91   mSocket = aSocket;
92 
93   AddIPDLReference();
94   gNeckoChild->SendPTCPSocketConstructor(this, mHost, mPort);
95   PTCPSocketChild::SendOpen(mHost, mPort, aUseSSL, aUseArrayBuffers);
96 }
97 
ReleaseIPDLReference()98 void TCPSocketChildBase::ReleaseIPDLReference() {
99   MOZ_ASSERT(mIPCOpen);
100   mIPCOpen = false;
101   mSocket = nullptr;
102   this->Release();
103 }
104 
AddIPDLReference()105 void TCPSocketChildBase::AddIPDLReference() {
106   MOZ_ASSERT(!mIPCOpen);
107   mIPCOpen = true;
108   this->AddRef();
109 }
110 
111 TCPSocketChild::~TCPSocketChild() = default;
112 
RecvUpdateBufferedAmount(const uint32_t & aBuffered,const uint32_t & aTrackingNumber)113 mozilla::ipc::IPCResult TCPSocketChild::RecvUpdateBufferedAmount(
114     const uint32_t& aBuffered, const uint32_t& aTrackingNumber) {
115   mSocket->UpdateBufferedAmount(aBuffered, aTrackingNumber);
116   return IPC_OK();
117 }
118 
RecvCallback(const nsString & aType,const CallbackData & aData,const uint32_t & aReadyState)119 mozilla::ipc::IPCResult TCPSocketChild::RecvCallback(
120     const nsString& aType, const CallbackData& aData,
121     const uint32_t& aReadyState) {
122   mSocket->UpdateReadyState(aReadyState);
123 
124   if (aData.type() == CallbackData::Tvoid_t) {
125     mSocket->FireEvent(aType);
126 
127   } else if (aData.type() == CallbackData::TTCPError) {
128     const TCPError& err(aData.get_TCPError());
129     mSocket->FireErrorEvent(err.name(), err.message(), err.errorCode());
130 
131   } else if (aData.type() == CallbackData::TSendableData) {
132     const SendableData& data = aData.get_SendableData();
133 
134     if (data.type() == SendableData::TArrayOfuint8_t) {
135       mSocket->FireDataArrayEvent(aType, data.get_ArrayOfuint8_t());
136     } else if (data.type() == SendableData::TnsCString) {
137       mSocket->FireDataStringEvent(aType, data.get_nsCString());
138     } else {
139       MOZ_CRASH("Invalid callback data type!");
140     }
141   } else {
142     MOZ_CRASH("Invalid callback type!");
143   }
144   return IPC_OK();
145 }
146 
SendSend(const nsACString & aData)147 void TCPSocketChild::SendSend(const nsACString& aData) {
148   SendData(nsCString(aData));
149 }
150 
SendSend(const ArrayBuffer & aData,uint32_t aByteOffset,uint32_t aByteLength)151 nsresult TCPSocketChild::SendSend(const ArrayBuffer& aData,
152                                   uint32_t aByteOffset, uint32_t aByteLength) {
153   uint32_t buflen = aData.Length();
154   uint32_t offset = std::min(buflen, aByteOffset);
155   uint32_t nbytes = std::min(buflen - aByteOffset, aByteLength);
156   FallibleTArray<uint8_t> fallibleArr;
157   if (!fallibleArr.InsertElementsAt(0, aData.Data() + offset, nbytes,
158                                     fallible)) {
159     return NS_ERROR_OUT_OF_MEMORY;
160   }
161 
162   SendData(SendableData{std::move(fallibleArr)});
163   return NS_OK;
164 }
165 
SetSocket(TCPSocket * aSocket)166 void TCPSocketChild::SetSocket(TCPSocket* aSocket) { mSocket = aSocket; }
167 
GetHost(nsAString & aHost)168 void TCPSocketChild::GetHost(nsAString& aHost) { aHost = mHost; }
169 
GetPort(uint16_t * aPort)170 void TCPSocketChild::GetPort(uint16_t* aPort) { *aPort = mPort; }
171 
RecvRequestDelete()172 mozilla::ipc::IPCResult TCPSocketChild::RecvRequestDelete() {
173   mozilla::Unused << Send__delete__(this);
174   return IPC_OK();
175 }
176 
177 }  // namespace mozilla::dom
178