|
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 |
|
4 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
5 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
7 |
|
8 // HttpLog.h should generally be included first |
|
9 #include "HttpLog.h" |
|
10 |
|
11 #include "nsHttp.h" |
|
12 #include "mozilla/unused.h" |
|
13 #include "mozilla/dom/ContentChild.h" |
|
14 #include "mozilla/dom/TabChild.h" |
|
15 #include "mozilla/dom/FileDescriptorSetChild.h" |
|
16 #include "mozilla/net/NeckoChild.h" |
|
17 #include "mozilla/net/HttpChannelChild.h" |
|
18 |
|
19 #include "nsStringStream.h" |
|
20 #include "nsHttpHandler.h" |
|
21 #include "nsNetUtil.h" |
|
22 #include "nsSerializationHelper.h" |
|
23 #include "mozilla/Attributes.h" |
|
24 #include "mozilla/ipc/InputStreamUtils.h" |
|
25 #include "mozilla/ipc/URIUtils.h" |
|
26 #include "mozilla/net/ChannelDiverterChild.h" |
|
27 #include "mozilla/net/DNS.h" |
|
28 #include "SerializedLoadContext.h" |
|
29 |
|
30 using namespace mozilla::dom; |
|
31 using namespace mozilla::ipc; |
|
32 |
|
33 namespace mozilla { |
|
34 namespace net { |
|
35 |
|
36 //----------------------------------------------------------------------------- |
|
37 // HttpChannelChild |
|
38 //----------------------------------------------------------------------------- |
|
39 |
|
40 HttpChannelChild::HttpChannelChild() |
|
41 : HttpAsyncAborter<HttpChannelChild>(MOZ_THIS_IN_INITIALIZER_LIST()) |
|
42 , mIsFromCache(false) |
|
43 , mCacheEntryAvailable(false) |
|
44 , mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME) |
|
45 , mSendResumeAt(false) |
|
46 , mIPCOpen(false) |
|
47 , mKeptAlive(false) |
|
48 , mDivertingToParent(false) |
|
49 , mFlushedForDiversion(false) |
|
50 , mSuspendSent(false) |
|
51 { |
|
52 LOG(("Creating HttpChannelChild @%x\n", this)); |
|
53 |
|
54 mChannelCreationTime = PR_Now(); |
|
55 mChannelCreationTimestamp = TimeStamp::Now(); |
|
56 mAsyncOpenTime = TimeStamp::Now(); |
|
57 mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this)); |
|
58 } |
|
59 |
|
60 HttpChannelChild::~HttpChannelChild() |
|
61 { |
|
62 LOG(("Destroying HttpChannelChild @%x\n", this)); |
|
63 } |
|
64 |
|
65 //----------------------------------------------------------------------------- |
|
66 // HttpChannelChild::nsISupports |
|
67 //----------------------------------------------------------------------------- |
|
68 |
|
69 // Override nsHashPropertyBag's AddRef: we don't need thread-safe refcnt |
|
70 NS_IMPL_ADDREF(HttpChannelChild) |
|
71 |
|
72 NS_IMETHODIMP_(MozExternalRefCountType) HttpChannelChild::Release() |
|
73 { |
|
74 NS_PRECONDITION(0 != mRefCnt, "dup release"); |
|
75 NS_ASSERT_OWNINGTHREAD(HttpChannelChild); |
|
76 --mRefCnt; |
|
77 NS_LOG_RELEASE(this, mRefCnt, "HttpChannelChild"); |
|
78 |
|
79 // Normally we Send_delete in OnStopRequest, but when we need to retain the |
|
80 // remote channel for security info IPDL itself holds 1 reference, so we |
|
81 // Send_delete when refCnt==1. But if !mIPCOpen, then there's nobody to send |
|
82 // to, so we fall through. |
|
83 if (mKeptAlive && mRefCnt == 1 && mIPCOpen) { |
|
84 mKeptAlive = false; |
|
85 // Send_delete calls NeckoChild::DeallocPHttpChannel, which will release |
|
86 // again to refcount==0 |
|
87 PHttpChannelChild::Send__delete__(this); |
|
88 return 0; |
|
89 } |
|
90 |
|
91 if (mRefCnt == 0) { |
|
92 mRefCnt = 1; /* stabilize */ |
|
93 delete this; |
|
94 return 0; |
|
95 } |
|
96 return mRefCnt; |
|
97 } |
|
98 |
|
99 NS_INTERFACE_MAP_BEGIN(HttpChannelChild) |
|
100 NS_INTERFACE_MAP_ENTRY(nsIRequest) |
|
101 NS_INTERFACE_MAP_ENTRY(nsIChannel) |
|
102 NS_INTERFACE_MAP_ENTRY(nsIHttpChannel) |
|
103 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal) |
|
104 NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel) |
|
105 NS_INTERFACE_MAP_ENTRY(nsIResumableChannel) |
|
106 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) |
|
107 NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel) |
|
108 NS_INTERFACE_MAP_ENTRY(nsITraceableChannel) |
|
109 NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer) |
|
110 NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel) |
|
111 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback) |
|
112 NS_INTERFACE_MAP_ENTRY(nsIChildChannel) |
|
113 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelChild) |
|
114 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAssociatedContentSecurity, GetAssociatedContentSecurity()) |
|
115 NS_INTERFACE_MAP_ENTRY(nsIDivertableChannel) |
|
116 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) |
|
117 |
|
118 //----------------------------------------------------------------------------- |
|
119 // HttpChannelChild::PHttpChannelChild |
|
120 //----------------------------------------------------------------------------- |
|
121 |
|
122 void |
|
123 HttpChannelChild::AddIPDLReference() |
|
124 { |
|
125 MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference"); |
|
126 mIPCOpen = true; |
|
127 AddRef(); |
|
128 } |
|
129 |
|
130 void |
|
131 HttpChannelChild::ReleaseIPDLReference() |
|
132 { |
|
133 MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference"); |
|
134 mIPCOpen = false; |
|
135 Release(); |
|
136 } |
|
137 |
|
138 class AssociateApplicationCacheEvent : public ChannelEvent |
|
139 { |
|
140 public: |
|
141 AssociateApplicationCacheEvent(HttpChannelChild* child, |
|
142 const nsCString &groupID, |
|
143 const nsCString &clientID) |
|
144 : mChild(child) |
|
145 , groupID(groupID) |
|
146 , clientID(clientID) {} |
|
147 |
|
148 void Run() { mChild->AssociateApplicationCache(groupID, clientID); } |
|
149 private: |
|
150 HttpChannelChild* mChild; |
|
151 nsCString groupID; |
|
152 nsCString clientID; |
|
153 }; |
|
154 |
|
155 bool |
|
156 HttpChannelChild::RecvAssociateApplicationCache(const nsCString &groupID, |
|
157 const nsCString &clientID) |
|
158 { |
|
159 if (mEventQ->ShouldEnqueue()) { |
|
160 mEventQ->Enqueue(new AssociateApplicationCacheEvent(this, groupID, clientID)); |
|
161 } else { |
|
162 AssociateApplicationCache(groupID, clientID); |
|
163 } |
|
164 return true; |
|
165 } |
|
166 |
|
167 void |
|
168 HttpChannelChild::AssociateApplicationCache(const nsCString &groupID, |
|
169 const nsCString &clientID) |
|
170 { |
|
171 nsresult rv; |
|
172 mApplicationCache = do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv); |
|
173 if (NS_FAILED(rv)) |
|
174 return; |
|
175 |
|
176 mLoadedFromApplicationCache = true; |
|
177 mApplicationCache->InitAsHandle(groupID, clientID); |
|
178 } |
|
179 |
|
180 class StartRequestEvent : public ChannelEvent |
|
181 { |
|
182 public: |
|
183 StartRequestEvent(HttpChannelChild* child, |
|
184 const nsresult& channelStatus, |
|
185 const nsHttpResponseHead& responseHead, |
|
186 const bool& useResponseHead, |
|
187 const nsHttpHeaderArray& requestHeaders, |
|
188 const bool& isFromCache, |
|
189 const bool& cacheEntryAvailable, |
|
190 const uint32_t& cacheExpirationTime, |
|
191 const nsCString& cachedCharset, |
|
192 const nsCString& securityInfoSerialization, |
|
193 const NetAddr& selfAddr, |
|
194 const NetAddr& peerAddr) |
|
195 : mChild(child) |
|
196 , mChannelStatus(channelStatus) |
|
197 , mResponseHead(responseHead) |
|
198 , mRequestHeaders(requestHeaders) |
|
199 , mUseResponseHead(useResponseHead) |
|
200 , mIsFromCache(isFromCache) |
|
201 , mCacheEntryAvailable(cacheEntryAvailable) |
|
202 , mCacheExpirationTime(cacheExpirationTime) |
|
203 , mCachedCharset(cachedCharset) |
|
204 , mSecurityInfoSerialization(securityInfoSerialization) |
|
205 , mSelfAddr(selfAddr) |
|
206 , mPeerAddr(peerAddr) |
|
207 {} |
|
208 |
|
209 void Run() |
|
210 { |
|
211 mChild->OnStartRequest(mChannelStatus, mResponseHead, mUseResponseHead, |
|
212 mRequestHeaders, mIsFromCache, mCacheEntryAvailable, |
|
213 mCacheExpirationTime, mCachedCharset, |
|
214 mSecurityInfoSerialization, mSelfAddr, mPeerAddr); |
|
215 } |
|
216 private: |
|
217 HttpChannelChild* mChild; |
|
218 nsresult mChannelStatus; |
|
219 nsHttpResponseHead mResponseHead; |
|
220 nsHttpHeaderArray mRequestHeaders; |
|
221 bool mUseResponseHead; |
|
222 bool mIsFromCache; |
|
223 bool mCacheEntryAvailable; |
|
224 uint32_t mCacheExpirationTime; |
|
225 nsCString mCachedCharset; |
|
226 nsCString mSecurityInfoSerialization; |
|
227 NetAddr mSelfAddr; |
|
228 NetAddr mPeerAddr; |
|
229 }; |
|
230 |
|
231 bool |
|
232 HttpChannelChild::RecvOnStartRequest(const nsresult& channelStatus, |
|
233 const nsHttpResponseHead& responseHead, |
|
234 const bool& useResponseHead, |
|
235 const nsHttpHeaderArray& requestHeaders, |
|
236 const bool& isFromCache, |
|
237 const bool& cacheEntryAvailable, |
|
238 const uint32_t& cacheExpirationTime, |
|
239 const nsCString& cachedCharset, |
|
240 const nsCString& securityInfoSerialization, |
|
241 const NetAddr& selfAddr, |
|
242 const NetAddr& peerAddr, |
|
243 const int16_t& redirectCount) |
|
244 { |
|
245 // mFlushedForDiversion and mDivertingToParent should NEVER be set at this |
|
246 // stage, as they are set in the listener's OnStartRequest. |
|
247 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
248 "mFlushedForDiversion should be unset before OnStartRequest!"); |
|
249 MOZ_RELEASE_ASSERT(!mDivertingToParent, |
|
250 "mDivertingToParent should be unset before OnStartRequest!"); |
|
251 |
|
252 |
|
253 mRedirectCount = redirectCount; |
|
254 |
|
255 if (mEventQ->ShouldEnqueue()) { |
|
256 mEventQ->Enqueue(new StartRequestEvent(this, channelStatus, responseHead, |
|
257 useResponseHead, requestHeaders, |
|
258 isFromCache, cacheEntryAvailable, |
|
259 cacheExpirationTime, cachedCharset, |
|
260 securityInfoSerialization, selfAddr, |
|
261 peerAddr)); |
|
262 } else { |
|
263 OnStartRequest(channelStatus, responseHead, useResponseHead, requestHeaders, |
|
264 isFromCache, cacheEntryAvailable, cacheExpirationTime, |
|
265 cachedCharset, securityInfoSerialization, selfAddr, |
|
266 peerAddr); |
|
267 } |
|
268 return true; |
|
269 } |
|
270 |
|
271 void |
|
272 HttpChannelChild::OnStartRequest(const nsresult& channelStatus, |
|
273 const nsHttpResponseHead& responseHead, |
|
274 const bool& useResponseHead, |
|
275 const nsHttpHeaderArray& requestHeaders, |
|
276 const bool& isFromCache, |
|
277 const bool& cacheEntryAvailable, |
|
278 const uint32_t& cacheExpirationTime, |
|
279 const nsCString& cachedCharset, |
|
280 const nsCString& securityInfoSerialization, |
|
281 const NetAddr& selfAddr, |
|
282 const NetAddr& peerAddr) |
|
283 { |
|
284 LOG(("HttpChannelChild::RecvOnStartRequest [this=%p]\n", this)); |
|
285 |
|
286 // mFlushedForDiversion and mDivertingToParent should NEVER be set at this |
|
287 // stage, as they are set in the listener's OnStartRequest. |
|
288 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
289 "mFlushedForDiversion should be unset before OnStartRequest!"); |
|
290 MOZ_RELEASE_ASSERT(!mDivertingToParent, |
|
291 "mDivertingToParent should be unset before OnStartRequest!"); |
|
292 |
|
293 if (!mCanceled && NS_SUCCEEDED(mStatus)) { |
|
294 mStatus = channelStatus; |
|
295 } |
|
296 |
|
297 if (useResponseHead && !mCanceled) |
|
298 mResponseHead = new nsHttpResponseHead(responseHead); |
|
299 |
|
300 if (!securityInfoSerialization.IsEmpty()) { |
|
301 NS_DeserializeObject(securityInfoSerialization, |
|
302 getter_AddRefs(mSecurityInfo)); |
|
303 } |
|
304 |
|
305 mIsFromCache = isFromCache; |
|
306 mCacheEntryAvailable = cacheEntryAvailable; |
|
307 mCacheExpirationTime = cacheExpirationTime; |
|
308 mCachedCharset = cachedCharset; |
|
309 |
|
310 AutoEventEnqueuer ensureSerialDispatch(mEventQ); |
|
311 |
|
312 // replace our request headers with what actually got sent in the parent |
|
313 mRequestHead.Headers() = requestHeaders; |
|
314 |
|
315 // Note: this is where we would notify "http-on-examine-response" observers. |
|
316 // We have deliberately disabled this for child processes (see bug 806753) |
|
317 // |
|
318 // gHttpHandler->OnExamineResponse(this); |
|
319 |
|
320 mTracingEnabled = false; |
|
321 |
|
322 nsresult rv = mListener->OnStartRequest(this, mListenerContext); |
|
323 if (NS_FAILED(rv)) { |
|
324 Cancel(rv); |
|
325 return; |
|
326 } |
|
327 |
|
328 if (mDivertingToParent) { |
|
329 mListener = nullptr; |
|
330 mListenerContext = nullptr; |
|
331 if (mLoadGroup) { |
|
332 mLoadGroup->RemoveRequest(this, nullptr, mStatus); |
|
333 } |
|
334 } |
|
335 |
|
336 if (mResponseHead) |
|
337 SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); |
|
338 |
|
339 rv = ApplyContentConversions(); |
|
340 if (NS_FAILED(rv)) |
|
341 Cancel(rv); |
|
342 |
|
343 mSelfAddr = selfAddr; |
|
344 mPeerAddr = peerAddr; |
|
345 } |
|
346 |
|
347 class TransportAndDataEvent : public ChannelEvent |
|
348 { |
|
349 public: |
|
350 TransportAndDataEvent(HttpChannelChild* child, |
|
351 const nsresult& channelStatus, |
|
352 const nsresult& transportStatus, |
|
353 const uint64_t& progress, |
|
354 const uint64_t& progressMax, |
|
355 const nsCString& data, |
|
356 const uint64_t& offset, |
|
357 const uint32_t& count) |
|
358 : mChild(child) |
|
359 , mChannelStatus(channelStatus) |
|
360 , mTransportStatus(transportStatus) |
|
361 , mProgress(progress) |
|
362 , mProgressMax(progressMax) |
|
363 , mData(data) |
|
364 , mOffset(offset) |
|
365 , mCount(count) {} |
|
366 |
|
367 void Run() |
|
368 { |
|
369 mChild->OnTransportAndData(mChannelStatus, mTransportStatus, mProgress, |
|
370 mProgressMax, mData, mOffset, mCount); |
|
371 } |
|
372 private: |
|
373 HttpChannelChild* mChild; |
|
374 nsresult mChannelStatus; |
|
375 nsresult mTransportStatus; |
|
376 uint64_t mProgress; |
|
377 uint64_t mProgressMax; |
|
378 nsCString mData; |
|
379 uint64_t mOffset; |
|
380 uint32_t mCount; |
|
381 }; |
|
382 |
|
383 bool |
|
384 HttpChannelChild::RecvOnTransportAndData(const nsresult& channelStatus, |
|
385 const nsresult& transportStatus, |
|
386 const uint64_t& progress, |
|
387 const uint64_t& progressMax, |
|
388 const nsCString& data, |
|
389 const uint64_t& offset, |
|
390 const uint32_t& count) |
|
391 { |
|
392 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
393 "Should not be receiving any more callbacks from parent!"); |
|
394 |
|
395 if (mEventQ->ShouldEnqueue()) { |
|
396 mEventQ->Enqueue(new TransportAndDataEvent(this, channelStatus, |
|
397 transportStatus, progress, |
|
398 progressMax, data, offset, |
|
399 count)); |
|
400 } else { |
|
401 MOZ_RELEASE_ASSERT(!mDivertingToParent, |
|
402 "ShouldEnqueue when diverting to parent!"); |
|
403 |
|
404 OnTransportAndData(channelStatus, transportStatus, progress, progressMax, |
|
405 data, offset, count); |
|
406 } |
|
407 return true; |
|
408 } |
|
409 |
|
410 void |
|
411 HttpChannelChild::OnTransportAndData(const nsresult& channelStatus, |
|
412 const nsresult& transportStatus, |
|
413 const uint64_t progress, |
|
414 const uint64_t& progressMax, |
|
415 const nsCString& data, |
|
416 const uint64_t& offset, |
|
417 const uint32_t& count) |
|
418 { |
|
419 LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this)); |
|
420 |
|
421 if (!mCanceled && NS_SUCCEEDED(mStatus)) { |
|
422 mStatus = channelStatus; |
|
423 } |
|
424 |
|
425 // For diversion to parent, just SendDivertOnDataAvailable. |
|
426 if (mDivertingToParent) { |
|
427 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
428 "Should not be processing any more callbacks from parent!"); |
|
429 |
|
430 SendDivertOnDataAvailable(data, offset, count); |
|
431 return; |
|
432 } |
|
433 |
|
434 if (mCanceled) |
|
435 return; |
|
436 |
|
437 // cache the progress sink so we don't have to query for it each time. |
|
438 if (!mProgressSink) |
|
439 GetCallback(mProgressSink); |
|
440 |
|
441 // Hold queue lock throughout all three calls, else we might process a later |
|
442 // necko msg in between them. |
|
443 AutoEventEnqueuer ensureSerialDispatch(mEventQ); |
|
444 |
|
445 // block status/progress after Cancel or OnStopRequest has been called, |
|
446 // or if channel has LOAD_BACKGROUND set. |
|
447 // - JDUELL: may not need mStatus/mIsPending checks, given this is always called |
|
448 // during OnDataAvailable, and we've already checked mCanceled. Code |
|
449 // dupe'd from nsHttpChannel |
|
450 if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && |
|
451 !(mLoadFlags & LOAD_BACKGROUND)) |
|
452 { |
|
453 // OnStatus |
|
454 // |
|
455 MOZ_ASSERT(transportStatus == NS_NET_STATUS_RECEIVING_FROM || |
|
456 transportStatus == NS_NET_STATUS_READING); |
|
457 |
|
458 nsAutoCString host; |
|
459 mURI->GetHost(host); |
|
460 mProgressSink->OnStatus(this, nullptr, transportStatus, |
|
461 NS_ConvertUTF8toUTF16(host).get()); |
|
462 // OnProgress |
|
463 // |
|
464 if (progress > 0) { |
|
465 MOZ_ASSERT(progress <= progressMax, "unexpected progress values"); |
|
466 mProgressSink->OnProgress(this, nullptr, progress, progressMax); |
|
467 } |
|
468 } |
|
469 |
|
470 // OnDataAvailable |
|
471 // |
|
472 // NOTE: the OnDataAvailable contract requires the client to read all the data |
|
473 // in the inputstream. This code relies on that ('data' will go away after |
|
474 // this function). Apparently the previous, non-e10s behavior was to actually |
|
475 // support only reading part of the data, allowing later calls to read the |
|
476 // rest. |
|
477 nsCOMPtr<nsIInputStream> stringStream; |
|
478 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(), |
|
479 count, NS_ASSIGNMENT_DEPEND); |
|
480 if (NS_FAILED(rv)) { |
|
481 Cancel(rv); |
|
482 return; |
|
483 } |
|
484 |
|
485 rv = mListener->OnDataAvailable(this, mListenerContext, |
|
486 stringStream, offset, count); |
|
487 stringStream->Close(); |
|
488 if (NS_FAILED(rv)) { |
|
489 Cancel(rv); |
|
490 } |
|
491 } |
|
492 |
|
493 class StopRequestEvent : public ChannelEvent |
|
494 { |
|
495 public: |
|
496 StopRequestEvent(HttpChannelChild* child, |
|
497 const nsresult& channelStatus) |
|
498 : mChild(child) |
|
499 , mChannelStatus(channelStatus) {} |
|
500 |
|
501 void Run() { mChild->OnStopRequest(mChannelStatus); } |
|
502 private: |
|
503 HttpChannelChild* mChild; |
|
504 nsresult mChannelStatus; |
|
505 }; |
|
506 |
|
507 bool |
|
508 HttpChannelChild::RecvOnStopRequest(const nsresult& channelStatus) |
|
509 { |
|
510 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
511 "Should not be receiving any more callbacks from parent!"); |
|
512 |
|
513 if (mEventQ->ShouldEnqueue()) { |
|
514 mEventQ->Enqueue(new StopRequestEvent(this, channelStatus)); |
|
515 } else { |
|
516 MOZ_ASSERT(!mDivertingToParent, "ShouldEnqueue when diverting to parent!"); |
|
517 |
|
518 OnStopRequest(channelStatus); |
|
519 } |
|
520 return true; |
|
521 } |
|
522 |
|
523 void |
|
524 HttpChannelChild::OnStopRequest(const nsresult& channelStatus) |
|
525 { |
|
526 LOG(("HttpChannelChild::OnStopRequest [this=%p status=%x]\n", |
|
527 this, channelStatus)); |
|
528 |
|
529 if (mDivertingToParent) { |
|
530 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
531 "Should not be processing any more callbacks from parent!"); |
|
532 |
|
533 SendDivertOnStopRequest(channelStatus); |
|
534 return; |
|
535 } |
|
536 |
|
537 mIsPending = false; |
|
538 |
|
539 if (!mCanceled && NS_SUCCEEDED(mStatus)) { |
|
540 mStatus = channelStatus; |
|
541 } |
|
542 |
|
543 { // We must flush the queue before we Send__delete__ |
|
544 // (although we really shouldn't receive any msgs after OnStop), |
|
545 // so make sure this goes out of scope before then. |
|
546 AutoEventEnqueuer ensureSerialDispatch(mEventQ); |
|
547 |
|
548 mListener->OnStopRequest(this, mListenerContext, mStatus); |
|
549 |
|
550 mListener = 0; |
|
551 mListenerContext = 0; |
|
552 mCacheEntryAvailable = false; |
|
553 if (mLoadGroup) |
|
554 mLoadGroup->RemoveRequest(this, nullptr, mStatus); |
|
555 } |
|
556 |
|
557 if (mLoadFlags & LOAD_DOCUMENT_URI) { |
|
558 // Keep IPDL channel open, but only for updating security info. |
|
559 mKeptAlive = true; |
|
560 SendDocumentChannelCleanup(); |
|
561 } else { |
|
562 // This calls NeckoChild::DeallocPHttpChannelChild(), which deletes |this| if IPDL |
|
563 // holds the last reference. Don't rely on |this| existing after here. |
|
564 PHttpChannelChild::Send__delete__(this); |
|
565 } |
|
566 } |
|
567 |
|
568 class ProgressEvent : public ChannelEvent |
|
569 { |
|
570 public: |
|
571 ProgressEvent(HttpChannelChild* child, |
|
572 const uint64_t& progress, |
|
573 const uint64_t& progressMax) |
|
574 : mChild(child) |
|
575 , mProgress(progress) |
|
576 , mProgressMax(progressMax) {} |
|
577 |
|
578 void Run() { mChild->OnProgress(mProgress, mProgressMax); } |
|
579 private: |
|
580 HttpChannelChild* mChild; |
|
581 uint64_t mProgress, mProgressMax; |
|
582 }; |
|
583 |
|
584 bool |
|
585 HttpChannelChild::RecvOnProgress(const uint64_t& progress, |
|
586 const uint64_t& progressMax) |
|
587 { |
|
588 if (mEventQ->ShouldEnqueue()) { |
|
589 mEventQ->Enqueue(new ProgressEvent(this, progress, progressMax)); |
|
590 } else { |
|
591 OnProgress(progress, progressMax); |
|
592 } |
|
593 return true; |
|
594 } |
|
595 |
|
596 void |
|
597 HttpChannelChild::OnProgress(const uint64_t& progress, |
|
598 const uint64_t& progressMax) |
|
599 { |
|
600 LOG(("HttpChannelChild::OnProgress [this=%p progress=%llu/%llu]\n", |
|
601 this, progress, progressMax)); |
|
602 |
|
603 if (mCanceled) |
|
604 return; |
|
605 |
|
606 // cache the progress sink so we don't have to query for it each time. |
|
607 if (!mProgressSink) |
|
608 GetCallback(mProgressSink); |
|
609 |
|
610 AutoEventEnqueuer ensureSerialDispatch(mEventQ); |
|
611 |
|
612 // block socket status event after Cancel or OnStopRequest has been called, |
|
613 // or if channel has LOAD_BACKGROUND set |
|
614 if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && |
|
615 !(mLoadFlags & LOAD_BACKGROUND)) |
|
616 { |
|
617 if (progress > 0) { |
|
618 MOZ_ASSERT(progress <= progressMax, "unexpected progress values"); |
|
619 mProgressSink->OnProgress(this, nullptr, progress, progressMax); |
|
620 } |
|
621 } |
|
622 } |
|
623 |
|
624 class StatusEvent : public ChannelEvent |
|
625 { |
|
626 public: |
|
627 StatusEvent(HttpChannelChild* child, |
|
628 const nsresult& status) |
|
629 : mChild(child) |
|
630 , mStatus(status) {} |
|
631 |
|
632 void Run() { mChild->OnStatus(mStatus); } |
|
633 private: |
|
634 HttpChannelChild* mChild; |
|
635 nsresult mStatus; |
|
636 }; |
|
637 |
|
638 bool |
|
639 HttpChannelChild::RecvOnStatus(const nsresult& status) |
|
640 { |
|
641 if (mEventQ->ShouldEnqueue()) { |
|
642 mEventQ->Enqueue(new StatusEvent(this, status)); |
|
643 } else { |
|
644 OnStatus(status); |
|
645 } |
|
646 return true; |
|
647 } |
|
648 |
|
649 void |
|
650 HttpChannelChild::OnStatus(const nsresult& status) |
|
651 { |
|
652 LOG(("HttpChannelChild::OnStatus [this=%p status=%x]\n", this, status)); |
|
653 |
|
654 if (mCanceled) |
|
655 return; |
|
656 |
|
657 // cache the progress sink so we don't have to query for it each time. |
|
658 if (!mProgressSink) |
|
659 GetCallback(mProgressSink); |
|
660 |
|
661 AutoEventEnqueuer ensureSerialDispatch(mEventQ); |
|
662 |
|
663 // block socket status event after Cancel or OnStopRequest has been called, |
|
664 // or if channel has LOAD_BACKGROUND set |
|
665 if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && |
|
666 !(mLoadFlags & LOAD_BACKGROUND)) |
|
667 { |
|
668 nsAutoCString host; |
|
669 mURI->GetHost(host); |
|
670 mProgressSink->OnStatus(this, nullptr, status, |
|
671 NS_ConvertUTF8toUTF16(host).get()); |
|
672 } |
|
673 } |
|
674 |
|
675 class FailedAsyncOpenEvent : public ChannelEvent |
|
676 { |
|
677 public: |
|
678 FailedAsyncOpenEvent(HttpChannelChild* child, const nsresult& status) |
|
679 : mChild(child) |
|
680 , mStatus(status) {} |
|
681 |
|
682 void Run() { mChild->FailedAsyncOpen(mStatus); } |
|
683 private: |
|
684 HttpChannelChild* mChild; |
|
685 nsresult mStatus; |
|
686 }; |
|
687 |
|
688 bool |
|
689 HttpChannelChild::RecvFailedAsyncOpen(const nsresult& status) |
|
690 { |
|
691 if (mEventQ->ShouldEnqueue()) { |
|
692 mEventQ->Enqueue(new FailedAsyncOpenEvent(this, status)); |
|
693 } else { |
|
694 FailedAsyncOpen(status); |
|
695 } |
|
696 return true; |
|
697 } |
|
698 |
|
699 // We need to have an implementation of this function just so that we can keep |
|
700 // all references to mCallOnResume of type HttpChannelChild: it's not OK in C++ |
|
701 // to set a member function ptr to a base class function. |
|
702 void |
|
703 HttpChannelChild::HandleAsyncAbort() |
|
704 { |
|
705 HttpAsyncAborter<HttpChannelChild>::HandleAsyncAbort(); |
|
706 } |
|
707 |
|
708 void |
|
709 HttpChannelChild::FailedAsyncOpen(const nsresult& status) |
|
710 { |
|
711 LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%x]\n", this, status)); |
|
712 |
|
713 mStatus = status; |
|
714 mIsPending = false; |
|
715 // We're already being called from IPDL, therefore already "async" |
|
716 HandleAsyncAbort(); |
|
717 } |
|
718 |
|
719 void |
|
720 HttpChannelChild::DoNotifyListenerCleanup() |
|
721 { |
|
722 if (mIPCOpen) |
|
723 PHttpChannelChild::Send__delete__(this); |
|
724 } |
|
725 |
|
726 class DeleteSelfEvent : public ChannelEvent |
|
727 { |
|
728 public: |
|
729 DeleteSelfEvent(HttpChannelChild* child) : mChild(child) {} |
|
730 void Run() { mChild->DeleteSelf(); } |
|
731 private: |
|
732 HttpChannelChild* mChild; |
|
733 }; |
|
734 |
|
735 bool |
|
736 HttpChannelChild::RecvDeleteSelf() |
|
737 { |
|
738 if (mEventQ->ShouldEnqueue()) { |
|
739 mEventQ->Enqueue(new DeleteSelfEvent(this)); |
|
740 } else { |
|
741 DeleteSelf(); |
|
742 } |
|
743 return true; |
|
744 } |
|
745 |
|
746 void |
|
747 HttpChannelChild::DeleteSelf() |
|
748 { |
|
749 Send__delete__(this); |
|
750 } |
|
751 |
|
752 class Redirect1Event : public ChannelEvent |
|
753 { |
|
754 public: |
|
755 Redirect1Event(HttpChannelChild* child, |
|
756 const uint32_t& newChannelId, |
|
757 const URIParams& newURI, |
|
758 const uint32_t& redirectFlags, |
|
759 const nsHttpResponseHead& responseHead) |
|
760 : mChild(child) |
|
761 , mNewChannelId(newChannelId) |
|
762 , mNewURI(newURI) |
|
763 , mRedirectFlags(redirectFlags) |
|
764 , mResponseHead(responseHead) {} |
|
765 |
|
766 void Run() |
|
767 { |
|
768 mChild->Redirect1Begin(mNewChannelId, mNewURI, mRedirectFlags, |
|
769 mResponseHead); |
|
770 } |
|
771 private: |
|
772 HttpChannelChild* mChild; |
|
773 uint32_t mNewChannelId; |
|
774 URIParams mNewURI; |
|
775 uint32_t mRedirectFlags; |
|
776 nsHttpResponseHead mResponseHead; |
|
777 }; |
|
778 |
|
779 bool |
|
780 HttpChannelChild::RecvRedirect1Begin(const uint32_t& newChannelId, |
|
781 const URIParams& newUri, |
|
782 const uint32_t& redirectFlags, |
|
783 const nsHttpResponseHead& responseHead) |
|
784 { |
|
785 if (mEventQ->ShouldEnqueue()) { |
|
786 mEventQ->Enqueue(new Redirect1Event(this, newChannelId, newUri, |
|
787 redirectFlags, responseHead)); |
|
788 } else { |
|
789 Redirect1Begin(newChannelId, newUri, redirectFlags, responseHead); |
|
790 } |
|
791 return true; |
|
792 } |
|
793 |
|
794 void |
|
795 HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId, |
|
796 const URIParams& newUri, |
|
797 const uint32_t& redirectFlags, |
|
798 const nsHttpResponseHead& responseHead) |
|
799 { |
|
800 nsresult rv; |
|
801 |
|
802 nsCOMPtr<nsIIOService> ioService; |
|
803 rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); |
|
804 if (NS_FAILED(rv)) { |
|
805 // Veto redirect. nsHttpChannel decides to cancel or continue. |
|
806 OnRedirectVerifyCallback(rv); |
|
807 return; |
|
808 } |
|
809 |
|
810 nsCOMPtr<nsIURI> uri = DeserializeURI(newUri); |
|
811 |
|
812 nsCOMPtr<nsIChannel> newChannel; |
|
813 rv = ioService->NewChannelFromURI(uri, getter_AddRefs(newChannel)); |
|
814 if (NS_FAILED(rv)) { |
|
815 // Veto redirect. nsHttpChannel decides to cancel or continue. |
|
816 OnRedirectVerifyCallback(rv); |
|
817 return; |
|
818 } |
|
819 |
|
820 // We won't get OnStartRequest, set cookies here. |
|
821 mResponseHead = new nsHttpResponseHead(responseHead); |
|
822 SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); |
|
823 |
|
824 bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(mResponseHead->Status(), |
|
825 mRequestHead.ParsedMethod()); |
|
826 |
|
827 rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET); |
|
828 if (NS_FAILED(rv)) { |
|
829 // Veto redirect. nsHttpChannel decides to cancel or continue. |
|
830 OnRedirectVerifyCallback(rv); |
|
831 return; |
|
832 } |
|
833 |
|
834 mRedirectChannelChild = do_QueryInterface(newChannel); |
|
835 if (mRedirectChannelChild) { |
|
836 mRedirectChannelChild->ConnectParent(newChannelId); |
|
837 rv = gHttpHandler->AsyncOnChannelRedirect(this, |
|
838 newChannel, |
|
839 redirectFlags); |
|
840 } else { |
|
841 LOG((" redirecting to a protocol that doesn't implement" |
|
842 " nsIChildChannel")); |
|
843 rv = NS_ERROR_FAILURE; |
|
844 } |
|
845 |
|
846 if (NS_FAILED(rv)) |
|
847 OnRedirectVerifyCallback(rv); |
|
848 } |
|
849 |
|
850 class Redirect3Event : public ChannelEvent |
|
851 { |
|
852 public: |
|
853 Redirect3Event(HttpChannelChild* child) : mChild(child) {} |
|
854 void Run() { mChild->Redirect3Complete(); } |
|
855 private: |
|
856 HttpChannelChild* mChild; |
|
857 }; |
|
858 |
|
859 bool |
|
860 HttpChannelChild::RecvRedirect3Complete() |
|
861 { |
|
862 if (mEventQ->ShouldEnqueue()) { |
|
863 mEventQ->Enqueue(new Redirect3Event(this)); |
|
864 } else { |
|
865 Redirect3Complete(); |
|
866 } |
|
867 return true; |
|
868 } |
|
869 |
|
870 class HttpFlushedForDiversionEvent : public ChannelEvent |
|
871 { |
|
872 public: |
|
873 HttpFlushedForDiversionEvent(HttpChannelChild* aChild) |
|
874 : mChild(aChild) |
|
875 { |
|
876 MOZ_RELEASE_ASSERT(aChild); |
|
877 } |
|
878 |
|
879 void Run() |
|
880 { |
|
881 mChild->FlushedForDiversion(); |
|
882 } |
|
883 private: |
|
884 HttpChannelChild* mChild; |
|
885 }; |
|
886 |
|
887 bool |
|
888 HttpChannelChild::RecvFlushedForDiversion() |
|
889 { |
|
890 MOZ_RELEASE_ASSERT(mDivertingToParent); |
|
891 MOZ_RELEASE_ASSERT(mEventQ->ShouldEnqueue()); |
|
892 |
|
893 mEventQ->Enqueue(new HttpFlushedForDiversionEvent(this)); |
|
894 |
|
895 return true; |
|
896 } |
|
897 |
|
898 void |
|
899 HttpChannelChild::FlushedForDiversion() |
|
900 { |
|
901 MOZ_RELEASE_ASSERT(mDivertingToParent); |
|
902 |
|
903 // Once this is set, it should not be unset before HttpChannelChild is taken |
|
904 // down. After it is set, no OnStart/OnData/OnStop callbacks should be |
|
905 // received from the parent channel, nor dequeued from the ChannelEventQueue. |
|
906 mFlushedForDiversion = true; |
|
907 |
|
908 SendDivertComplete(); |
|
909 } |
|
910 |
|
911 bool |
|
912 HttpChannelChild::RecvDivertMessages() |
|
913 { |
|
914 MOZ_RELEASE_ASSERT(mDivertingToParent); |
|
915 MOZ_RELEASE_ASSERT(mSuspendCount > 0); |
|
916 |
|
917 // DivertTo() has been called on parent, so we can now start sending queued |
|
918 // IPDL messages back to parent listener. |
|
919 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(Resume())); |
|
920 |
|
921 return true; |
|
922 } |
|
923 |
|
924 void |
|
925 HttpChannelChild::Redirect3Complete() |
|
926 { |
|
927 nsresult rv = NS_OK; |
|
928 |
|
929 // Chrome channel has been AsyncOpen'd. Reflect this in child. |
|
930 if (mRedirectChannelChild) |
|
931 rv = mRedirectChannelChild->CompleteRedirectSetup(mListener, |
|
932 mListenerContext); |
|
933 |
|
934 // Redirecting to new channel: shut this down and init new channel |
|
935 if (mLoadGroup) |
|
936 mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_ABORTED); |
|
937 |
|
938 if (NS_FAILED(rv)) |
|
939 NS_WARNING("CompleteRedirectSetup failed, HttpChannelChild already open?"); |
|
940 |
|
941 // Release ref to new channel. |
|
942 mRedirectChannelChild = nullptr; |
|
943 } |
|
944 |
|
945 //----------------------------------------------------------------------------- |
|
946 // HttpChannelChild::nsIChildChannel |
|
947 //----------------------------------------------------------------------------- |
|
948 |
|
949 NS_IMETHODIMP |
|
950 HttpChannelChild::ConnectParent(uint32_t id) |
|
951 { |
|
952 mozilla::dom::TabChild* tabChild = nullptr; |
|
953 nsCOMPtr<nsITabChild> iTabChild; |
|
954 GetCallback(iTabChild); |
|
955 if (iTabChild) { |
|
956 tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get()); |
|
957 } |
|
958 if (MissingRequiredTabChild(tabChild, "http")) { |
|
959 return NS_ERROR_ILLEGAL_VALUE; |
|
960 } |
|
961 |
|
962 // The socket transport in the chrome process now holds a logical ref to us |
|
963 // until OnStopRequest, or we do a redirect, or we hit an IPDL error. |
|
964 AddIPDLReference(); |
|
965 |
|
966 HttpChannelConnectArgs connectArgs(id); |
|
967 if (!gNeckoChild-> |
|
968 SendPHttpChannelConstructor(this, tabChild, |
|
969 IPC::SerializedLoadContext(this), |
|
970 connectArgs)) { |
|
971 return NS_ERROR_FAILURE; |
|
972 } |
|
973 |
|
974 return NS_OK; |
|
975 } |
|
976 |
|
977 NS_IMETHODIMP |
|
978 HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener, |
|
979 nsISupports *aContext) |
|
980 { |
|
981 LOG(("HttpChannelChild::FinishRedirectSetup [this=%p]\n", this)); |
|
982 |
|
983 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); |
|
984 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); |
|
985 |
|
986 /* |
|
987 * No need to check for cancel: we don't get here if nsHttpChannel canceled |
|
988 * before AsyncOpen(); if it's canceled after that, OnStart/Stop will just |
|
989 * get called with error code as usual. So just setup mListener and make the |
|
990 * channel reflect AsyncOpen'ed state. |
|
991 */ |
|
992 |
|
993 mIsPending = true; |
|
994 mWasOpened = true; |
|
995 mListener = listener; |
|
996 mListenerContext = aContext; |
|
997 |
|
998 // add ourselves to the load group. |
|
999 if (mLoadGroup) |
|
1000 mLoadGroup->AddRequest(this, nullptr); |
|
1001 |
|
1002 // We already have an open IPDL connection to the parent. If on-modify-request |
|
1003 // listeners or load group observers canceled us, let the parent handle it |
|
1004 // and send it back to us naturally. |
|
1005 return NS_OK; |
|
1006 } |
|
1007 |
|
1008 //----------------------------------------------------------------------------- |
|
1009 // HttpChannelChild::nsIAsyncVerifyRedirectCallback |
|
1010 //----------------------------------------------------------------------------- |
|
1011 |
|
1012 NS_IMETHODIMP |
|
1013 HttpChannelChild::OnRedirectVerifyCallback(nsresult result) |
|
1014 { |
|
1015 OptionalURIParams redirectURI; |
|
1016 nsCOMPtr<nsIHttpChannel> newHttpChannel = |
|
1017 do_QueryInterface(mRedirectChannelChild); |
|
1018 |
|
1019 if (newHttpChannel) { |
|
1020 // Must not be called until after redirect observers called. |
|
1021 newHttpChannel->SetOriginalURI(mOriginalURI); |
|
1022 } |
|
1023 |
|
1024 RequestHeaderTuples emptyHeaders; |
|
1025 RequestHeaderTuples* headerTuples = &emptyHeaders; |
|
1026 |
|
1027 nsCOMPtr<nsIHttpChannelChild> newHttpChannelChild = |
|
1028 do_QueryInterface(mRedirectChannelChild); |
|
1029 if (newHttpChannelChild && NS_SUCCEEDED(result)) { |
|
1030 newHttpChannelChild->AddCookiesToRequest(); |
|
1031 newHttpChannelChild->GetClientSetRequestHeaders(&headerTuples); |
|
1032 } |
|
1033 |
|
1034 /* If the redirect was canceled, bypass OMR and send an empty API |
|
1035 * redirect URI */ |
|
1036 SerializeURI(nullptr, redirectURI); |
|
1037 |
|
1038 if (NS_SUCCEEDED(result)) { |
|
1039 // Note: this is where we would notify "http-on-modify-response" observers. |
|
1040 // We have deliberately disabled this for child processes (see bug 806753) |
|
1041 // |
|
1042 // After we verify redirect, nsHttpChannel may hit the network: must give |
|
1043 // "http-on-modify-request" observers the chance to cancel before that. |
|
1044 //base->CallOnModifyRequestObservers(); |
|
1045 |
|
1046 nsCOMPtr<nsIHttpChannelInternal> newHttpChannelInternal = |
|
1047 do_QueryInterface(mRedirectChannelChild); |
|
1048 if (newHttpChannelInternal) { |
|
1049 nsCOMPtr<nsIURI> apiRedirectURI; |
|
1050 nsresult rv = newHttpChannelInternal->GetApiRedirectToURI( |
|
1051 getter_AddRefs(apiRedirectURI)); |
|
1052 if (NS_SUCCEEDED(rv) && apiRedirectURI) { |
|
1053 /* If there was an API redirect of this channel, we need to send it |
|
1054 * up here, since it can't be sent via SendAsyncOpen. */ |
|
1055 SerializeURI(apiRedirectURI, redirectURI); |
|
1056 } |
|
1057 } |
|
1058 } |
|
1059 |
|
1060 if (mIPCOpen) |
|
1061 SendRedirect2Verify(result, *headerTuples, redirectURI); |
|
1062 |
|
1063 return NS_OK; |
|
1064 } |
|
1065 |
|
1066 //----------------------------------------------------------------------------- |
|
1067 // HttpChannelChild::nsIRequest |
|
1068 //----------------------------------------------------------------------------- |
|
1069 |
|
1070 NS_IMETHODIMP |
|
1071 HttpChannelChild::Cancel(nsresult status) |
|
1072 { |
|
1073 MOZ_ASSERT(NS_IsMainThread()); |
|
1074 |
|
1075 if (!mCanceled) { |
|
1076 // If this cancel occurs before nsHttpChannel has been set up, AsyncOpen |
|
1077 // is responsible for cleaning up. |
|
1078 mCanceled = true; |
|
1079 mStatus = status; |
|
1080 if (RemoteChannelExists()) |
|
1081 SendCancel(status); |
|
1082 } |
|
1083 return NS_OK; |
|
1084 } |
|
1085 |
|
1086 NS_IMETHODIMP |
|
1087 HttpChannelChild::Suspend() |
|
1088 { |
|
1089 NS_ENSURE_TRUE(RemoteChannelExists(), NS_ERROR_NOT_AVAILABLE); |
|
1090 |
|
1091 // SendSuspend only once, when suspend goes from 0 to 1. |
|
1092 // Don't SendSuspend at all if we're diverting callbacks to the parent; |
|
1093 // suspend will be called at the correct time in the parent itself. |
|
1094 if (!mSuspendCount++ && !mDivertingToParent) { |
|
1095 SendSuspend(); |
|
1096 mSuspendSent = true; |
|
1097 } |
|
1098 mEventQ->Suspend(); |
|
1099 |
|
1100 return NS_OK; |
|
1101 } |
|
1102 |
|
1103 NS_IMETHODIMP |
|
1104 HttpChannelChild::Resume() |
|
1105 { |
|
1106 NS_ENSURE_TRUE(RemoteChannelExists(), NS_ERROR_NOT_AVAILABLE); |
|
1107 NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED); |
|
1108 |
|
1109 nsresult rv = NS_OK; |
|
1110 |
|
1111 // SendResume only once, when suspend count drops to 0. |
|
1112 // Don't SendResume at all if we're diverting callbacks to the parent (unless |
|
1113 // suspend was sent earlier); otherwise, resume will be called at the correct |
|
1114 // time in the parent itself. |
|
1115 if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) { |
|
1116 SendResume(); |
|
1117 if (mCallOnResume) { |
|
1118 AsyncCall(mCallOnResume); |
|
1119 mCallOnResume = nullptr; |
|
1120 } |
|
1121 } |
|
1122 mEventQ->Resume(); |
|
1123 |
|
1124 return rv; |
|
1125 } |
|
1126 |
|
1127 //----------------------------------------------------------------------------- |
|
1128 // HttpChannelChild::nsIChannel |
|
1129 //----------------------------------------------------------------------------- |
|
1130 |
|
1131 NS_IMETHODIMP |
|
1132 HttpChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo) |
|
1133 { |
|
1134 NS_ENSURE_ARG_POINTER(aSecurityInfo); |
|
1135 NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); |
|
1136 return NS_OK; |
|
1137 } |
|
1138 |
|
1139 NS_IMETHODIMP |
|
1140 HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) |
|
1141 { |
|
1142 LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get())); |
|
1143 |
|
1144 if (mCanceled) |
|
1145 return mStatus; |
|
1146 |
|
1147 NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE); |
|
1148 NS_ENSURE_ARG_POINTER(listener); |
|
1149 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); |
|
1150 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); |
|
1151 |
|
1152 mAsyncOpenTime = TimeStamp::Now(); |
|
1153 |
|
1154 // Port checked in parent, but duplicate here so we can return with error |
|
1155 // immediately |
|
1156 nsresult rv; |
|
1157 rv = NS_CheckPortSafety(mURI); |
|
1158 if (NS_FAILED(rv)) |
|
1159 return rv; |
|
1160 |
|
1161 const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie); |
|
1162 if (cookieHeader) { |
|
1163 mUserSetCookieHeader = cookieHeader; |
|
1164 } |
|
1165 |
|
1166 AddCookiesToRequest(); |
|
1167 |
|
1168 // |
|
1169 // NOTE: From now on we must return NS_OK; all errors must be handled via |
|
1170 // OnStart/OnStopRequest |
|
1171 // |
|
1172 |
|
1173 // Note: this is where we would notify "http-on-modify-request" observers. |
|
1174 // We have deliberately disabled this for child processes (see bug 806753) |
|
1175 // |
|
1176 // notify "http-on-modify-request" observers |
|
1177 //CallOnModifyRequestObservers(); |
|
1178 |
|
1179 mIsPending = true; |
|
1180 mWasOpened = true; |
|
1181 mListener = listener; |
|
1182 mListenerContext = aContext; |
|
1183 |
|
1184 // add ourselves to the load group. |
|
1185 if (mLoadGroup) |
|
1186 mLoadGroup->AddRequest(this, nullptr); |
|
1187 |
|
1188 if (mCanceled) { |
|
1189 // We may have been canceled already, either by on-modify-request |
|
1190 // listeners or by load group observers; in that case, don't create IPDL |
|
1191 // connection. See nsHttpChannel::AsyncOpen(). |
|
1192 AsyncAbort(mStatus); |
|
1193 return NS_OK; |
|
1194 } |
|
1195 |
|
1196 nsCString appCacheClientId; |
|
1197 if (mInheritApplicationCache) { |
|
1198 // Pick up an application cache from the notification |
|
1199 // callbacks if available |
|
1200 nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer; |
|
1201 GetCallback(appCacheContainer); |
|
1202 |
|
1203 if (appCacheContainer) { |
|
1204 nsCOMPtr<nsIApplicationCache> appCache; |
|
1205 rv = appCacheContainer->GetApplicationCache(getter_AddRefs(appCache)); |
|
1206 if (NS_SUCCEEDED(rv) && appCache) { |
|
1207 appCache->GetClientID(appCacheClientId); |
|
1208 } |
|
1209 } |
|
1210 } |
|
1211 |
|
1212 // |
|
1213 // Send request to the chrome process... |
|
1214 // |
|
1215 |
|
1216 mozilla::dom::TabChild* tabChild = nullptr; |
|
1217 nsCOMPtr<nsITabChild> iTabChild; |
|
1218 GetCallback(iTabChild); |
|
1219 if (iTabChild) { |
|
1220 tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get()); |
|
1221 } |
|
1222 if (MissingRequiredTabChild(tabChild, "http")) { |
|
1223 return NS_ERROR_ILLEGAL_VALUE; |
|
1224 } |
|
1225 |
|
1226 HttpChannelOpenArgs openArgs; |
|
1227 // No access to HttpChannelOpenArgs members, but they each have a |
|
1228 // function with the struct name that returns a ref. |
|
1229 SerializeURI(mURI, openArgs.uri()); |
|
1230 SerializeURI(mOriginalURI, openArgs.original()); |
|
1231 SerializeURI(mDocumentURI, openArgs.doc()); |
|
1232 SerializeURI(mReferrer, openArgs.referrer()); |
|
1233 SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo()); |
|
1234 openArgs.loadFlags() = mLoadFlags; |
|
1235 openArgs.requestHeaders() = mClientSetRequestHeaders; |
|
1236 openArgs.requestMethod() = mRequestHead.Method(); |
|
1237 |
|
1238 nsTArray<mozilla::ipc::FileDescriptor> fds; |
|
1239 SerializeInputStream(mUploadStream, openArgs.uploadStream(), fds); |
|
1240 |
|
1241 PFileDescriptorSetChild* fdSet = nullptr; |
|
1242 if (!fds.IsEmpty()) { |
|
1243 MOZ_ASSERT(gNeckoChild->Manager()); |
|
1244 |
|
1245 fdSet = gNeckoChild->Manager()->SendPFileDescriptorSetConstructor(fds[0]); |
|
1246 for (uint32_t i = 1; i < fds.Length(); ++i) { |
|
1247 unused << fdSet->SendAddFileDescriptor(fds[i]); |
|
1248 } |
|
1249 } |
|
1250 |
|
1251 OptionalFileDescriptorSet optionalFDs; |
|
1252 if (fdSet) { |
|
1253 optionalFDs = fdSet; |
|
1254 } else { |
|
1255 optionalFDs = mozilla::void_t(); |
|
1256 } |
|
1257 |
|
1258 openArgs.fds() = optionalFDs; |
|
1259 |
|
1260 openArgs.uploadStreamHasHeaders() = mUploadStreamHasHeaders; |
|
1261 openArgs.priority() = mPriority; |
|
1262 openArgs.redirectionLimit() = mRedirectionLimit; |
|
1263 openArgs.allowPipelining() = mAllowPipelining; |
|
1264 openArgs.forceAllowThirdPartyCookie() = mForceAllowThirdPartyCookie; |
|
1265 openArgs.resumeAt() = mSendResumeAt; |
|
1266 openArgs.startPos() = mStartPos; |
|
1267 openArgs.entityID() = mEntityID; |
|
1268 openArgs.chooseApplicationCache() = mChooseApplicationCache; |
|
1269 openArgs.appCacheClientID() = appCacheClientId; |
|
1270 openArgs.allowSpdy() = mAllowSpdy; |
|
1271 |
|
1272 // The socket transport in the chrome process now holds a logical ref to us |
|
1273 // until OnStopRequest, or we do a redirect, or we hit an IPDL error. |
|
1274 AddIPDLReference(); |
|
1275 |
|
1276 gNeckoChild->SendPHttpChannelConstructor(this, tabChild, |
|
1277 IPC::SerializedLoadContext(this), |
|
1278 openArgs); |
|
1279 |
|
1280 if (fdSet) { |
|
1281 FileDescriptorSetChild* fdSetActor = |
|
1282 static_cast<FileDescriptorSetChild*>(fdSet); |
|
1283 |
|
1284 fdSetActor->ForgetFileDescriptors(fds); |
|
1285 } |
|
1286 |
|
1287 return NS_OK; |
|
1288 } |
|
1289 |
|
1290 //----------------------------------------------------------------------------- |
|
1291 // HttpChannelChild::nsIHttpChannel |
|
1292 //----------------------------------------------------------------------------- |
|
1293 |
|
1294 NS_IMETHODIMP |
|
1295 HttpChannelChild::SetRequestHeader(const nsACString& aHeader, |
|
1296 const nsACString& aValue, |
|
1297 bool aMerge) |
|
1298 { |
|
1299 nsresult rv = HttpBaseChannel::SetRequestHeader(aHeader, aValue, aMerge); |
|
1300 if (NS_FAILED(rv)) |
|
1301 return rv; |
|
1302 |
|
1303 RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement(); |
|
1304 if (!tuple) |
|
1305 return NS_ERROR_OUT_OF_MEMORY; |
|
1306 |
|
1307 tuple->mHeader = aHeader; |
|
1308 tuple->mValue = aValue; |
|
1309 tuple->mMerge = aMerge; |
|
1310 return NS_OK; |
|
1311 } |
|
1312 |
|
1313 NS_IMETHODIMP |
|
1314 HttpChannelChild::RedirectTo(nsIURI *newURI) |
|
1315 { |
|
1316 // disabled until/unless addons run in child or something else needs this |
|
1317 return NS_ERROR_NOT_IMPLEMENTED; |
|
1318 } |
|
1319 |
|
1320 //----------------------------------------------------------------------------- |
|
1321 // HttpChannelChild::nsIHttpChannelInternal |
|
1322 //----------------------------------------------------------------------------- |
|
1323 |
|
1324 NS_IMETHODIMP |
|
1325 HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey) |
|
1326 { |
|
1327 DROP_DEAD(); |
|
1328 } |
|
1329 |
|
1330 // The next four _should_ be implemented, but we need to figure out how |
|
1331 // to transfer the data from the chrome process first. |
|
1332 |
|
1333 NS_IMETHODIMP |
|
1334 HttpChannelChild::GetRemoteAddress(nsACString & _result) |
|
1335 { |
|
1336 return NS_ERROR_NOT_AVAILABLE; |
|
1337 } |
|
1338 |
|
1339 NS_IMETHODIMP |
|
1340 HttpChannelChild::GetRemotePort(int32_t * _result) |
|
1341 { |
|
1342 NS_ENSURE_ARG_POINTER(_result); |
|
1343 return NS_ERROR_NOT_AVAILABLE; |
|
1344 } |
|
1345 |
|
1346 NS_IMETHODIMP |
|
1347 HttpChannelChild::GetLocalAddress(nsACString & _result) |
|
1348 { |
|
1349 return NS_ERROR_NOT_AVAILABLE; |
|
1350 } |
|
1351 |
|
1352 NS_IMETHODIMP |
|
1353 HttpChannelChild::GetLocalPort(int32_t * _result) |
|
1354 { |
|
1355 NS_ENSURE_ARG_POINTER(_result); |
|
1356 return NS_ERROR_NOT_AVAILABLE; |
|
1357 } |
|
1358 |
|
1359 |
|
1360 //----------------------------------------------------------------------------- |
|
1361 // HttpChannelChild::nsICacheInfoChannel |
|
1362 //----------------------------------------------------------------------------- |
|
1363 |
|
1364 NS_IMETHODIMP |
|
1365 HttpChannelChild::GetCacheTokenExpirationTime(uint32_t *_retval) |
|
1366 { |
|
1367 NS_ENSURE_ARG_POINTER(_retval); |
|
1368 if (!mCacheEntryAvailable) |
|
1369 return NS_ERROR_NOT_AVAILABLE; |
|
1370 |
|
1371 *_retval = mCacheExpirationTime; |
|
1372 return NS_OK; |
|
1373 } |
|
1374 |
|
1375 NS_IMETHODIMP |
|
1376 HttpChannelChild::GetCacheTokenCachedCharset(nsACString &_retval) |
|
1377 { |
|
1378 if (!mCacheEntryAvailable) |
|
1379 return NS_ERROR_NOT_AVAILABLE; |
|
1380 |
|
1381 _retval = mCachedCharset; |
|
1382 return NS_OK; |
|
1383 } |
|
1384 NS_IMETHODIMP |
|
1385 HttpChannelChild::SetCacheTokenCachedCharset(const nsACString &aCharset) |
|
1386 { |
|
1387 if (!mCacheEntryAvailable || !RemoteChannelExists()) |
|
1388 return NS_ERROR_NOT_AVAILABLE; |
|
1389 |
|
1390 mCachedCharset = aCharset; |
|
1391 if (!SendSetCacheTokenCachedCharset(PromiseFlatCString(aCharset))) { |
|
1392 return NS_ERROR_FAILURE; |
|
1393 } |
|
1394 return NS_OK; |
|
1395 } |
|
1396 |
|
1397 NS_IMETHODIMP |
|
1398 HttpChannelChild::IsFromCache(bool *value) |
|
1399 { |
|
1400 if (!mIsPending) |
|
1401 return NS_ERROR_NOT_AVAILABLE; |
|
1402 |
|
1403 *value = mIsFromCache; |
|
1404 return NS_OK; |
|
1405 } |
|
1406 |
|
1407 //----------------------------------------------------------------------------- |
|
1408 // HttpChannelChild::nsIResumableChannel |
|
1409 //----------------------------------------------------------------------------- |
|
1410 |
|
1411 NS_IMETHODIMP |
|
1412 HttpChannelChild::ResumeAt(uint64_t startPos, const nsACString& entityID) |
|
1413 { |
|
1414 ENSURE_CALLED_BEFORE_CONNECT(); |
|
1415 mStartPos = startPos; |
|
1416 mEntityID = entityID; |
|
1417 mSendResumeAt = true; |
|
1418 return NS_OK; |
|
1419 } |
|
1420 |
|
1421 // GetEntityID is shared in HttpBaseChannel |
|
1422 |
|
1423 //----------------------------------------------------------------------------- |
|
1424 // HttpChannelChild::nsISupportsPriority |
|
1425 //----------------------------------------------------------------------------- |
|
1426 |
|
1427 NS_IMETHODIMP |
|
1428 HttpChannelChild::SetPriority(int32_t aPriority) |
|
1429 { |
|
1430 int16_t newValue = clamped<int32_t>(aPriority, INT16_MIN, INT16_MAX); |
|
1431 if (mPriority == newValue) |
|
1432 return NS_OK; |
|
1433 mPriority = newValue; |
|
1434 if (RemoteChannelExists()) |
|
1435 SendSetPriority(mPriority); |
|
1436 return NS_OK; |
|
1437 } |
|
1438 |
|
1439 //----------------------------------------------------------------------------- |
|
1440 // HttpChannelChild::nsIProxiedChannel |
|
1441 //----------------------------------------------------------------------------- |
|
1442 |
|
1443 NS_IMETHODIMP |
|
1444 HttpChannelChild::GetProxyInfo(nsIProxyInfo **aProxyInfo) |
|
1445 { |
|
1446 DROP_DEAD(); |
|
1447 } |
|
1448 |
|
1449 //----------------------------------------------------------------------------- |
|
1450 // HttpChannelChild::nsIApplicationCacheContainer |
|
1451 //----------------------------------------------------------------------------- |
|
1452 |
|
1453 NS_IMETHODIMP |
|
1454 HttpChannelChild::GetApplicationCache(nsIApplicationCache **aApplicationCache) |
|
1455 { |
|
1456 NS_IF_ADDREF(*aApplicationCache = mApplicationCache); |
|
1457 return NS_OK; |
|
1458 } |
|
1459 NS_IMETHODIMP |
|
1460 HttpChannelChild::SetApplicationCache(nsIApplicationCache *aApplicationCache) |
|
1461 { |
|
1462 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); |
|
1463 |
|
1464 mApplicationCache = aApplicationCache; |
|
1465 return NS_OK; |
|
1466 } |
|
1467 |
|
1468 //----------------------------------------------------------------------------- |
|
1469 // HttpChannelChild::nsIApplicationCacheChannel |
|
1470 //----------------------------------------------------------------------------- |
|
1471 |
|
1472 NS_IMETHODIMP |
|
1473 HttpChannelChild::GetApplicationCacheForWrite(nsIApplicationCache **aApplicationCache) |
|
1474 { |
|
1475 *aApplicationCache = nullptr; |
|
1476 return NS_OK; |
|
1477 } |
|
1478 NS_IMETHODIMP |
|
1479 HttpChannelChild::SetApplicationCacheForWrite(nsIApplicationCache *aApplicationCache) |
|
1480 { |
|
1481 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); |
|
1482 |
|
1483 // Child channels are not intended to be used for cache writes |
|
1484 return NS_ERROR_NOT_IMPLEMENTED; |
|
1485 } |
|
1486 |
|
1487 NS_IMETHODIMP |
|
1488 HttpChannelChild::GetLoadedFromApplicationCache(bool *aLoadedFromApplicationCache) |
|
1489 { |
|
1490 *aLoadedFromApplicationCache = mLoadedFromApplicationCache; |
|
1491 return NS_OK; |
|
1492 } |
|
1493 |
|
1494 NS_IMETHODIMP |
|
1495 HttpChannelChild::GetInheritApplicationCache(bool *aInherit) |
|
1496 { |
|
1497 *aInherit = mInheritApplicationCache; |
|
1498 return NS_OK; |
|
1499 } |
|
1500 NS_IMETHODIMP |
|
1501 HttpChannelChild::SetInheritApplicationCache(bool aInherit) |
|
1502 { |
|
1503 mInheritApplicationCache = aInherit; |
|
1504 return NS_OK; |
|
1505 } |
|
1506 |
|
1507 NS_IMETHODIMP |
|
1508 HttpChannelChild::GetChooseApplicationCache(bool *aChoose) |
|
1509 { |
|
1510 *aChoose = mChooseApplicationCache; |
|
1511 return NS_OK; |
|
1512 } |
|
1513 |
|
1514 NS_IMETHODIMP |
|
1515 HttpChannelChild::SetChooseApplicationCache(bool aChoose) |
|
1516 { |
|
1517 mChooseApplicationCache = aChoose; |
|
1518 return NS_OK; |
|
1519 } |
|
1520 |
|
1521 NS_IMETHODIMP |
|
1522 HttpChannelChild::MarkOfflineCacheEntryAsForeign() |
|
1523 { |
|
1524 SendMarkOfflineCacheEntryAsForeign(); |
|
1525 return NS_OK; |
|
1526 } |
|
1527 |
|
1528 //----------------------------------------------------------------------------- |
|
1529 // HttpChannelChild::nsIAssociatedContentSecurity |
|
1530 //----------------------------------------------------------------------------- |
|
1531 |
|
1532 bool |
|
1533 HttpChannelChild::GetAssociatedContentSecurity( |
|
1534 nsIAssociatedContentSecurity** _result) |
|
1535 { |
|
1536 if (!mSecurityInfo) |
|
1537 return false; |
|
1538 |
|
1539 nsCOMPtr<nsIAssociatedContentSecurity> assoc = |
|
1540 do_QueryInterface(mSecurityInfo); |
|
1541 if (!assoc) |
|
1542 return false; |
|
1543 |
|
1544 if (_result) |
|
1545 assoc.forget(_result); |
|
1546 return true; |
|
1547 } |
|
1548 |
|
1549 /* attribute unsigned long countSubRequestsBrokenSecurity; */ |
|
1550 NS_IMETHODIMP |
|
1551 HttpChannelChild::GetCountSubRequestsBrokenSecurity( |
|
1552 int32_t *aSubRequestsBrokenSecurity) |
|
1553 { |
|
1554 nsCOMPtr<nsIAssociatedContentSecurity> assoc; |
|
1555 if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) |
|
1556 return NS_OK; |
|
1557 |
|
1558 return assoc->GetCountSubRequestsBrokenSecurity(aSubRequestsBrokenSecurity); |
|
1559 } |
|
1560 NS_IMETHODIMP |
|
1561 HttpChannelChild::SetCountSubRequestsBrokenSecurity( |
|
1562 int32_t aSubRequestsBrokenSecurity) |
|
1563 { |
|
1564 nsCOMPtr<nsIAssociatedContentSecurity> assoc; |
|
1565 if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) |
|
1566 return NS_OK; |
|
1567 |
|
1568 return assoc->SetCountSubRequestsBrokenSecurity(aSubRequestsBrokenSecurity); |
|
1569 } |
|
1570 |
|
1571 /* attribute unsigned long countSubRequestsNoSecurity; */ |
|
1572 NS_IMETHODIMP |
|
1573 HttpChannelChild::GetCountSubRequestsNoSecurity(int32_t *aSubRequestsNoSecurity) |
|
1574 { |
|
1575 nsCOMPtr<nsIAssociatedContentSecurity> assoc; |
|
1576 if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) |
|
1577 return NS_OK; |
|
1578 |
|
1579 return assoc->GetCountSubRequestsNoSecurity(aSubRequestsNoSecurity); |
|
1580 } |
|
1581 NS_IMETHODIMP |
|
1582 HttpChannelChild::SetCountSubRequestsNoSecurity(int32_t aSubRequestsNoSecurity) |
|
1583 { |
|
1584 nsCOMPtr<nsIAssociatedContentSecurity> assoc; |
|
1585 if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) |
|
1586 return NS_OK; |
|
1587 |
|
1588 return assoc->SetCountSubRequestsNoSecurity(aSubRequestsNoSecurity); |
|
1589 } |
|
1590 |
|
1591 NS_IMETHODIMP |
|
1592 HttpChannelChild::Flush() |
|
1593 { |
|
1594 nsCOMPtr<nsIAssociatedContentSecurity> assoc; |
|
1595 if (!GetAssociatedContentSecurity(getter_AddRefs(assoc))) |
|
1596 return NS_OK; |
|
1597 |
|
1598 nsresult rv; |
|
1599 int32_t broken, no; |
|
1600 |
|
1601 rv = assoc->GetCountSubRequestsBrokenSecurity(&broken); |
|
1602 NS_ENSURE_SUCCESS(rv, rv); |
|
1603 rv = assoc->GetCountSubRequestsNoSecurity(&no); |
|
1604 NS_ENSURE_SUCCESS(rv, rv); |
|
1605 |
|
1606 if (mIPCOpen) |
|
1607 SendUpdateAssociatedContentSecurity(broken, no); |
|
1608 |
|
1609 return NS_OK; |
|
1610 } |
|
1611 |
|
1612 //----------------------------------------------------------------------------- |
|
1613 // HttpChannelChild::nsIHttpChannelChild |
|
1614 //----------------------------------------------------------------------------- |
|
1615 |
|
1616 NS_IMETHODIMP HttpChannelChild::AddCookiesToRequest() |
|
1617 { |
|
1618 HttpBaseChannel::AddCookiesToRequest(); |
|
1619 return NS_OK; |
|
1620 } |
|
1621 |
|
1622 NS_IMETHODIMP HttpChannelChild::GetClientSetRequestHeaders(RequestHeaderTuples **aRequestHeaders) |
|
1623 { |
|
1624 *aRequestHeaders = &mClientSetRequestHeaders; |
|
1625 return NS_OK; |
|
1626 } |
|
1627 |
|
1628 //----------------------------------------------------------------------------- |
|
1629 // HttpChannelChild::nsIDivertableChannel |
|
1630 //----------------------------------------------------------------------------- |
|
1631 NS_IMETHODIMP |
|
1632 HttpChannelChild::DivertToParent(ChannelDiverterChild **aChild) |
|
1633 { |
|
1634 MOZ_RELEASE_ASSERT(aChild); |
|
1635 MOZ_RELEASE_ASSERT(gNeckoChild); |
|
1636 MOZ_RELEASE_ASSERT(!mDivertingToParent); |
|
1637 |
|
1638 // We must fail DivertToParent() if there's no parent end of the channel (and |
|
1639 // won't be!) due to early failure. |
|
1640 if (NS_FAILED(mStatus) && !RemoteChannelExists()) { |
|
1641 return mStatus; |
|
1642 } |
|
1643 |
|
1644 nsresult rv = Suspend(); |
|
1645 if (NS_WARN_IF(NS_FAILED(rv))) { |
|
1646 return rv; |
|
1647 } |
|
1648 |
|
1649 // Once this is set, it should not be unset before the child is taken down. |
|
1650 mDivertingToParent = true; |
|
1651 |
|
1652 PChannelDiverterChild* diverter = |
|
1653 gNeckoChild->SendPChannelDiverterConstructor(this); |
|
1654 MOZ_RELEASE_ASSERT(diverter); |
|
1655 |
|
1656 *aChild = static_cast<ChannelDiverterChild*>(diverter); |
|
1657 |
|
1658 return NS_OK; |
|
1659 } |
|
1660 |
|
1661 }} // mozilla::net |