|
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 "TCPSocketParent.h" |
|
6 #include "jsapi.h" |
|
7 #include "jsfriendapi.h" |
|
8 #include "nsJSUtils.h" |
|
9 #include "nsIDOMTCPSocket.h" |
|
10 #include "nsCxPusher.h" |
|
11 #include "mozilla/unused.h" |
|
12 #include "mozilla/AppProcessChecker.h" |
|
13 #include "mozilla/net/NeckoCommon.h" |
|
14 #include "mozilla/net/PNeckoParent.h" |
|
15 #include "mozilla/dom/ContentParent.h" |
|
16 #include "mozilla/dom/TabParent.h" |
|
17 #include "nsIScriptSecurityManager.h" |
|
18 |
|
19 namespace IPC { |
|
20 |
|
21 //Defined in TCPSocketChild.cpp |
|
22 extern bool |
|
23 DeserializeArrayBuffer(JS::Handle<JSObject*> aObj, |
|
24 const InfallibleTArray<uint8_t>& aBuffer, |
|
25 JS::MutableHandle<JS::Value> aVal); |
|
26 |
|
27 } |
|
28 |
|
29 namespace mozilla { |
|
30 namespace dom { |
|
31 |
|
32 static void |
|
33 FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo) |
|
34 { |
|
35 mozilla::unused << |
|
36 aActor->SendCallback(NS_LITERAL_STRING("onerror"), |
|
37 TCPError(NS_LITERAL_STRING("InvalidStateError")), |
|
38 NS_LITERAL_STRING("connecting")); |
|
39 } |
|
40 |
|
41 NS_IMPL_CYCLE_COLLECTION(TCPSocketParentBase, mSocket, mIntermediary) |
|
42 NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketParentBase) |
|
43 NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketParentBase) |
|
44 |
|
45 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketParentBase) |
|
46 NS_INTERFACE_MAP_ENTRY(nsITCPSocketParent) |
|
47 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
48 NS_INTERFACE_MAP_END |
|
49 |
|
50 TCPSocketParentBase::TCPSocketParentBase() |
|
51 : mIPCOpen(false) |
|
52 { |
|
53 } |
|
54 |
|
55 TCPSocketParentBase::~TCPSocketParentBase() |
|
56 { |
|
57 } |
|
58 |
|
59 void |
|
60 TCPSocketParentBase::ReleaseIPDLReference() |
|
61 { |
|
62 MOZ_ASSERT(mIPCOpen); |
|
63 mIPCOpen = false; |
|
64 this->Release(); |
|
65 } |
|
66 |
|
67 void |
|
68 TCPSocketParentBase::AddIPDLReference() |
|
69 { |
|
70 MOZ_ASSERT(!mIPCOpen); |
|
71 mIPCOpen = true; |
|
72 this->AddRef(); |
|
73 } |
|
74 |
|
75 NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketParent::Release(void) |
|
76 { |
|
77 nsrefcnt refcnt = TCPSocketParentBase::Release(); |
|
78 if (refcnt == 1 && mIPCOpen) { |
|
79 mozilla::unused << PTCPSocketParent::SendRequestDelete(); |
|
80 return 1; |
|
81 } |
|
82 return refcnt; |
|
83 } |
|
84 |
|
85 bool |
|
86 TCPSocketParent::RecvOpen(const nsString& aHost, const uint16_t& aPort, const bool& aUseSSL, |
|
87 const nsString& aBinaryType) |
|
88 { |
|
89 // We don't have browser actors in xpcshell, and hence can't run automated |
|
90 // tests without this loophole. |
|
91 if (net::UsingNeckoIPCSecurity() && |
|
92 !AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) { |
|
93 FireInteralError(this, __LINE__); |
|
94 return true; |
|
95 } |
|
96 |
|
97 // Obtain App ID |
|
98 uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; |
|
99 const PContentParent *content = Manager()->Manager(); |
|
100 const InfallibleTArray<PBrowserParent*>& browsers = content->ManagedPBrowserParent(); |
|
101 if (browsers.Length() > 0) { |
|
102 TabParent *tab = static_cast<TabParent*>(browsers[0]); |
|
103 appId = tab->OwnAppId(); |
|
104 } |
|
105 |
|
106 nsresult rv; |
|
107 mIntermediary = do_CreateInstance("@mozilla.org/tcp-socket-intermediary;1", &rv); |
|
108 if (NS_FAILED(rv)) { |
|
109 FireInteralError(this, __LINE__); |
|
110 return true; |
|
111 } |
|
112 |
|
113 rv = mIntermediary->Open(this, aHost, aPort, aUseSSL, aBinaryType, appId, |
|
114 getter_AddRefs(mSocket)); |
|
115 if (NS_FAILED(rv) || !mSocket) { |
|
116 FireInteralError(this, __LINE__); |
|
117 return true; |
|
118 } |
|
119 |
|
120 return true; |
|
121 } |
|
122 |
|
123 NS_IMETHODIMP |
|
124 TCPSocketParent::InitJS(JS::Handle<JS::Value> aIntermediary, JSContext* aCx) |
|
125 { |
|
126 MOZ_ASSERT(aIntermediary.isObject()); |
|
127 mIntermediaryObj = &aIntermediary.toObject(); |
|
128 return NS_OK; |
|
129 } |
|
130 |
|
131 bool |
|
132 TCPSocketParent::RecvStartTLS() |
|
133 { |
|
134 NS_ENSURE_TRUE(mSocket, true); |
|
135 nsresult rv = mSocket->UpgradeToSecure(); |
|
136 NS_ENSURE_SUCCESS(rv, true); |
|
137 return true; |
|
138 } |
|
139 |
|
140 bool |
|
141 TCPSocketParent::RecvSuspend() |
|
142 { |
|
143 NS_ENSURE_TRUE(mSocket, true); |
|
144 nsresult rv = mSocket->Suspend(); |
|
145 NS_ENSURE_SUCCESS(rv, true); |
|
146 return true; |
|
147 } |
|
148 |
|
149 bool |
|
150 TCPSocketParent::RecvResume() |
|
151 { |
|
152 NS_ENSURE_TRUE(mSocket, true); |
|
153 nsresult rv = mSocket->Resume(); |
|
154 NS_ENSURE_SUCCESS(rv, true); |
|
155 return true; |
|
156 } |
|
157 |
|
158 bool |
|
159 TCPSocketParent::RecvData(const SendableData& aData, |
|
160 const uint32_t& aTrackingNumber) |
|
161 { |
|
162 NS_ENSURE_TRUE(mIntermediary, true); |
|
163 |
|
164 nsresult rv; |
|
165 switch (aData.type()) { |
|
166 case SendableData::TArrayOfuint8_t: { |
|
167 AutoSafeJSContext cx; |
|
168 JSAutoRequest ar(cx); |
|
169 JS::Rooted<JS::Value> val(cx); |
|
170 JS::Rooted<JSObject*> obj(cx, mIntermediaryObj); |
|
171 IPC::DeserializeArrayBuffer(obj, aData.get_ArrayOfuint8_t(), &val); |
|
172 rv = mIntermediary->OnRecvSendArrayBuffer(val, aTrackingNumber); |
|
173 NS_ENSURE_SUCCESS(rv, true); |
|
174 break; |
|
175 } |
|
176 |
|
177 case SendableData::TnsString: |
|
178 rv = mIntermediary->OnRecvSendString(aData.get_nsString(), aTrackingNumber); |
|
179 NS_ENSURE_SUCCESS(rv, true); |
|
180 break; |
|
181 |
|
182 default: |
|
183 MOZ_CRASH("unexpected SendableData type"); |
|
184 } |
|
185 return true; |
|
186 } |
|
187 |
|
188 bool |
|
189 TCPSocketParent::RecvClose() |
|
190 { |
|
191 NS_ENSURE_TRUE(mSocket, true); |
|
192 nsresult rv = mSocket->Close(); |
|
193 NS_ENSURE_SUCCESS(rv, true); |
|
194 return true; |
|
195 } |
|
196 |
|
197 NS_IMETHODIMP |
|
198 TCPSocketParent::SendEvent(const nsAString& aType, JS::Handle<JS::Value> aDataVal, |
|
199 const nsAString& aReadyState, JSContext* aCx) |
|
200 { |
|
201 if (!mIPCOpen) { |
|
202 NS_WARNING("Dropping callback due to no IPC connection"); |
|
203 return NS_OK; |
|
204 } |
|
205 |
|
206 CallbackData data; |
|
207 if (aDataVal.isString()) { |
|
208 JSString* jsstr = aDataVal.toString(); |
|
209 nsDependentJSString str; |
|
210 if (!str.init(aCx, jsstr)) { |
|
211 FireInteralError(this, __LINE__); |
|
212 return NS_ERROR_OUT_OF_MEMORY; |
|
213 } |
|
214 data = SendableData(str); |
|
215 |
|
216 } else if (aDataVal.isUndefined() || aDataVal.isNull()) { |
|
217 data = mozilla::void_t(); |
|
218 |
|
219 } else if (aDataVal.isObject()) { |
|
220 JS::Rooted<JSObject *> obj(aCx, &aDataVal.toObject()); |
|
221 if (JS_IsArrayBufferObject(obj)) { |
|
222 uint32_t nbytes = JS_GetArrayBufferByteLength(obj); |
|
223 uint8_t* buffer = JS_GetArrayBufferData(obj); |
|
224 if (!buffer) { |
|
225 FireInteralError(this, __LINE__); |
|
226 return NS_ERROR_OUT_OF_MEMORY; |
|
227 } |
|
228 FallibleTArray<uint8_t> fallibleArr; |
|
229 if (!fallibleArr.InsertElementsAt(0, buffer, nbytes)) { |
|
230 FireInteralError(this, __LINE__); |
|
231 return NS_ERROR_OUT_OF_MEMORY; |
|
232 } |
|
233 InfallibleTArray<uint8_t> arr; |
|
234 arr.SwapElements(fallibleArr); |
|
235 data = SendableData(arr); |
|
236 |
|
237 } else { |
|
238 nsDependentJSString name; |
|
239 |
|
240 JS::Rooted<JS::Value> val(aCx); |
|
241 if (!JS_GetProperty(aCx, obj, "name", &val)) { |
|
242 NS_ERROR("No name property on supposed error object"); |
|
243 } else if (JSVAL_IS_STRING(val)) { |
|
244 if (!name.init(aCx, JSVAL_TO_STRING(val))) { |
|
245 NS_WARNING("couldn't initialize string"); |
|
246 } |
|
247 } |
|
248 |
|
249 data = TCPError(name); |
|
250 } |
|
251 } else { |
|
252 NS_ERROR("Unexpected JS value encountered"); |
|
253 FireInteralError(this, __LINE__); |
|
254 return NS_ERROR_FAILURE; |
|
255 } |
|
256 mozilla::unused << |
|
257 PTCPSocketParent::SendCallback(nsString(aType), data, |
|
258 nsString(aReadyState)); |
|
259 return NS_OK; |
|
260 } |
|
261 |
|
262 NS_IMETHODIMP |
|
263 TCPSocketParent::SetSocketAndIntermediary(nsIDOMTCPSocket *socket, |
|
264 nsITCPSocketIntermediary *intermediary, |
|
265 JSContext* cx) |
|
266 { |
|
267 mSocket = socket; |
|
268 mIntermediary = intermediary; |
|
269 return NS_OK; |
|
270 } |
|
271 |
|
272 NS_IMETHODIMP |
|
273 TCPSocketParent::SendUpdateBufferedAmount(uint32_t aBufferedAmount, |
|
274 uint32_t aTrackingNumber) |
|
275 { |
|
276 mozilla::unused << PTCPSocketParent::SendUpdateBufferedAmount(aBufferedAmount, |
|
277 aTrackingNumber); |
|
278 return NS_OK; |
|
279 } |
|
280 |
|
281 void |
|
282 TCPSocketParent::ActorDestroy(ActorDestroyReason why) |
|
283 { |
|
284 if (mSocket) { |
|
285 mSocket->Close(); |
|
286 } |
|
287 mSocket = nullptr; |
|
288 mIntermediaryObj = nullptr; |
|
289 mIntermediary = nullptr; |
|
290 } |
|
291 |
|
292 bool |
|
293 TCPSocketParent::RecvRequestDelete() |
|
294 { |
|
295 mozilla::unused << Send__delete__(this); |
|
296 return true; |
|
297 } |
|
298 |
|
299 } // namespace dom |
|
300 } // namespace mozilla |