|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include <algorithm> |
|
6 #include "TCPSocketChild.h" |
|
7 #include "mozilla/unused.h" |
|
8 #include "mozilla/net/NeckoChild.h" |
|
9 #include "mozilla/dom/PBrowserChild.h" |
|
10 #include "mozilla/dom/TabChild.h" |
|
11 #include "nsIDOMTCPSocket.h" |
|
12 #include "nsJSUtils.h" |
|
13 #include "nsContentUtils.h" |
|
14 #include "jsapi.h" |
|
15 #include "jsfriendapi.h" |
|
16 #include "jswrapper.h" |
|
17 |
|
18 using mozilla::net::gNeckoChild; |
|
19 |
|
20 namespace IPC { |
|
21 |
|
22 bool |
|
23 DeserializeArrayBuffer(JS::Handle<JSObject*> aObj, |
|
24 const InfallibleTArray<uint8_t>& aBuffer, |
|
25 JS::MutableHandle<JS::Value> aVal) |
|
26 { |
|
27 mozilla::AutoSafeJSContext cx; |
|
28 JSAutoCompartment ac(cx, aObj); |
|
29 |
|
30 JS::Rooted<JSObject*> obj(cx, JS_NewArrayBuffer(cx, aBuffer.Length())); |
|
31 if (!obj) |
|
32 return false; |
|
33 uint8_t* data = JS_GetArrayBufferData(obj); |
|
34 if (!data) |
|
35 return false; |
|
36 memcpy(data, aBuffer.Elements(), aBuffer.Length()); |
|
37 aVal.set(OBJECT_TO_JSVAL(obj)); |
|
38 return true; |
|
39 } |
|
40 |
|
41 } // namespace IPC |
|
42 |
|
43 namespace mozilla { |
|
44 namespace dom { |
|
45 |
|
46 NS_IMPL_CYCLE_COLLECTION(TCPSocketChildBase, mSocket) |
|
47 NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketChildBase) |
|
48 NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketChildBase) |
|
49 |
|
50 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketChildBase) |
|
51 NS_INTERFACE_MAP_ENTRY(nsITCPSocketChild) |
|
52 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
53 NS_INTERFACE_MAP_END |
|
54 |
|
55 TCPSocketChildBase::TCPSocketChildBase() |
|
56 : mIPCOpen(false) |
|
57 { |
|
58 } |
|
59 |
|
60 TCPSocketChildBase::~TCPSocketChildBase() |
|
61 { |
|
62 } |
|
63 |
|
64 NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketChild::Release(void) |
|
65 { |
|
66 nsrefcnt refcnt = TCPSocketChildBase::Release(); |
|
67 if (refcnt == 1 && mIPCOpen) { |
|
68 PTCPSocketChild::SendRequestDelete(); |
|
69 return 1; |
|
70 } |
|
71 return refcnt; |
|
72 } |
|
73 |
|
74 TCPSocketChild::TCPSocketChild() |
|
75 : mWindowObj(nullptr) |
|
76 { |
|
77 } |
|
78 |
|
79 NS_IMETHODIMP |
|
80 TCPSocketChild::SendOpen(nsITCPSocketInternal* aSocket, |
|
81 const nsAString& aHost, uint16_t aPort, |
|
82 bool aUseSSL, const nsAString& aBinaryType, |
|
83 nsIDOMWindow* aWindow, JS::Handle<JS::Value> aWindowObj, |
|
84 JSContext* aCx) |
|
85 { |
|
86 mSocket = aSocket; |
|
87 |
|
88 MOZ_ASSERT(aWindowObj.isObject()); |
|
89 mWindowObj = js::CheckedUnwrap(&aWindowObj.toObject()); |
|
90 if (!mWindowObj) { |
|
91 return NS_ERROR_FAILURE; |
|
92 } |
|
93 AddIPDLReference(); |
|
94 gNeckoChild->SendPTCPSocketConstructor(this); |
|
95 PTCPSocketChild::SendOpen(nsString(aHost), aPort, |
|
96 aUseSSL, nsString(aBinaryType)); |
|
97 return NS_OK; |
|
98 } |
|
99 |
|
100 void |
|
101 TCPSocketChildBase::ReleaseIPDLReference() |
|
102 { |
|
103 MOZ_ASSERT(mIPCOpen); |
|
104 mIPCOpen = false; |
|
105 this->Release(); |
|
106 } |
|
107 |
|
108 void |
|
109 TCPSocketChildBase::AddIPDLReference() |
|
110 { |
|
111 MOZ_ASSERT(!mIPCOpen); |
|
112 mIPCOpen = true; |
|
113 this->AddRef(); |
|
114 } |
|
115 |
|
116 TCPSocketChild::~TCPSocketChild() |
|
117 { |
|
118 } |
|
119 |
|
120 bool |
|
121 TCPSocketChild::RecvUpdateBufferedAmount(const uint32_t& aBuffered, |
|
122 const uint32_t& aTrackingNumber) |
|
123 { |
|
124 if (NS_FAILED(mSocket->UpdateBufferedAmount(aBuffered, aTrackingNumber))) { |
|
125 NS_ERROR("Shouldn't fail!"); |
|
126 } |
|
127 return true; |
|
128 } |
|
129 |
|
130 bool |
|
131 TCPSocketChild::RecvCallback(const nsString& aType, |
|
132 const CallbackData& aData, |
|
133 const nsString& aReadyState) |
|
134 { |
|
135 if (NS_FAILED(mSocket->UpdateReadyState(aReadyState))) |
|
136 NS_ERROR("Shouldn't fail!"); |
|
137 |
|
138 nsresult rv = NS_ERROR_FAILURE; |
|
139 if (aData.type() == CallbackData::Tvoid_t) { |
|
140 rv = mSocket->CallListenerVoid(aType); |
|
141 |
|
142 } else if (aData.type() == CallbackData::TTCPError) { |
|
143 const TCPError& err(aData.get_TCPError()); |
|
144 rv = mSocket->CallListenerError(aType, err.name()); |
|
145 |
|
146 } else if (aData.type() == CallbackData::TSendableData) { |
|
147 const SendableData& data = aData.get_SendableData(); |
|
148 |
|
149 if (data.type() == SendableData::TArrayOfuint8_t) { |
|
150 JSContext* cx = nsContentUtils::GetSafeJSContext(); |
|
151 JSAutoRequest ar(cx); |
|
152 JS::Rooted<JS::Value> val(cx); |
|
153 JS::Rooted<JSObject*> window(cx, mWindowObj); |
|
154 bool ok = IPC::DeserializeArrayBuffer(window, data.get_ArrayOfuint8_t(), &val); |
|
155 NS_ENSURE_TRUE(ok, true); |
|
156 rv = mSocket->CallListenerArrayBuffer(aType, val); |
|
157 |
|
158 } else if (data.type() == SendableData::TnsString) { |
|
159 rv = mSocket->CallListenerData(aType, data.get_nsString()); |
|
160 |
|
161 } else { |
|
162 MOZ_CRASH("Invalid callback data type!"); |
|
163 } |
|
164 |
|
165 } else { |
|
166 MOZ_CRASH("Invalid callback type!"); |
|
167 } |
|
168 NS_ENSURE_SUCCESS(rv, true); |
|
169 return true; |
|
170 } |
|
171 |
|
172 NS_IMETHODIMP |
|
173 TCPSocketChild::SendStartTLS() |
|
174 { |
|
175 PTCPSocketChild::SendStartTLS(); |
|
176 return NS_OK; |
|
177 } |
|
178 |
|
179 NS_IMETHODIMP |
|
180 TCPSocketChild::SendSuspend() |
|
181 { |
|
182 PTCPSocketChild::SendSuspend(); |
|
183 return NS_OK; |
|
184 } |
|
185 |
|
186 NS_IMETHODIMP |
|
187 TCPSocketChild::SendResume() |
|
188 { |
|
189 PTCPSocketChild::SendResume(); |
|
190 return NS_OK; |
|
191 } |
|
192 |
|
193 NS_IMETHODIMP |
|
194 TCPSocketChild::SendClose() |
|
195 { |
|
196 PTCPSocketChild::SendClose(); |
|
197 return NS_OK; |
|
198 } |
|
199 |
|
200 NS_IMETHODIMP |
|
201 TCPSocketChild::SendSend(JS::Handle<JS::Value> aData, |
|
202 uint32_t aByteOffset, |
|
203 uint32_t aByteLength, |
|
204 uint32_t aTrackingNumber, |
|
205 JSContext* aCx) |
|
206 { |
|
207 if (aData.isString()) { |
|
208 JSString* jsstr = aData.toString(); |
|
209 nsDependentJSString str; |
|
210 bool ok = str.init(aCx, jsstr); |
|
211 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); |
|
212 SendData(str, aTrackingNumber); |
|
213 } else { |
|
214 NS_ENSURE_TRUE(aData.isObject(), NS_ERROR_FAILURE); |
|
215 JS::Rooted<JSObject*> obj(aCx, &aData.toObject()); |
|
216 NS_ENSURE_TRUE(JS_IsArrayBufferObject(obj), NS_ERROR_FAILURE); |
|
217 uint32_t buflen = JS_GetArrayBufferByteLength(obj); |
|
218 aByteOffset = std::min(buflen, aByteOffset); |
|
219 uint32_t nbytes = std::min(buflen - aByteOffset, aByteLength); |
|
220 uint8_t* data = JS_GetArrayBufferData(obj); |
|
221 if (!data) { |
|
222 return NS_ERROR_OUT_OF_MEMORY; |
|
223 } |
|
224 FallibleTArray<uint8_t> fallibleArr; |
|
225 if (!fallibleArr.InsertElementsAt(0, data + aByteOffset, nbytes)) { |
|
226 return NS_ERROR_OUT_OF_MEMORY; |
|
227 } |
|
228 InfallibleTArray<uint8_t> arr; |
|
229 arr.SwapElements(fallibleArr); |
|
230 SendData(arr, aTrackingNumber); |
|
231 } |
|
232 return NS_OK; |
|
233 } |
|
234 |
|
235 NS_IMETHODIMP |
|
236 TCPSocketChild::SetSocketAndWindow(nsITCPSocketInternal *aSocket, |
|
237 JS::Handle<JS::Value> aWindowObj, |
|
238 JSContext* aCx) |
|
239 { |
|
240 mSocket = aSocket; |
|
241 MOZ_ASSERT(aWindowObj.isObject()); |
|
242 mWindowObj = js::CheckedUnwrap(&aWindowObj.toObject()); |
|
243 if (!mWindowObj) { |
|
244 return NS_ERROR_FAILURE; |
|
245 } |
|
246 return NS_OK; |
|
247 } |
|
248 |
|
249 bool |
|
250 TCPSocketChild::RecvRequestDelete() |
|
251 { |
|
252 mozilla::unused << Send__delete__(this); |
|
253 return true; |
|
254 } |
|
255 |
|
256 } // namespace dom |
|
257 } // namespace mozilla |