1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:set ts=4 sts=4 sw=4 et cin: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsIOService.h"
40 : #include "nsInputStreamPump.h"
41 : #include "nsIServiceManager.h"
42 : #include "nsIStreamTransportService.h"
43 : #include "nsIInterfaceRequestorUtils.h"
44 : #include "nsISeekableStream.h"
45 : #include "nsITransport.h"
46 : #include "nsNetUtil.h"
47 : #include "nsThreadUtils.h"
48 : #include "nsCOMPtr.h"
49 : #include "prlog.h"
50 : #include "sampler.h"
51 :
52 : static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
53 :
54 : #if defined(PR_LOGGING)
55 : //
56 : // NSPR_LOG_MODULES=nsStreamPump:5
57 : //
58 : static PRLogModuleInfo *gStreamPumpLog = nsnull;
59 : #endif
60 : #define LOG(args) PR_LOG(gStreamPumpLog, PR_LOG_DEBUG, args)
61 :
62 : //-----------------------------------------------------------------------------
63 : // nsInputStreamPump methods
64 : //-----------------------------------------------------------------------------
65 :
66 3499 : nsInputStreamPump::nsInputStreamPump()
67 : : mState(STATE_IDLE)
68 : , mStreamOffset(0)
69 3499 : , mStreamLength(LL_MaxUint())
70 : , mStatus(NS_OK)
71 : , mSuspendCount(0)
72 : , mLoadFlags(LOAD_NORMAL)
73 : , mWaiting(false)
74 6998 : , mCloseWhenDone(false)
75 : {
76 : #if defined(PR_LOGGING)
77 3499 : if (!gStreamPumpLog)
78 341 : gStreamPumpLog = PR_NewLogModule("nsStreamPump");
79 : #endif
80 3499 : }
81 :
82 3487 : nsInputStreamPump::~nsInputStreamPump()
83 : {
84 3487 : }
85 :
86 : nsresult
87 3396 : nsInputStreamPump::Create(nsInputStreamPump **result,
88 : nsIInputStream *stream,
89 : PRInt64 streamPos,
90 : PRInt64 streamLen,
91 : PRUint32 segsize,
92 : PRUint32 segcount,
93 : bool closeWhenDone)
94 : {
95 3396 : nsresult rv = NS_ERROR_OUT_OF_MEMORY;
96 6792 : nsRefPtr<nsInputStreamPump> pump = new nsInputStreamPump();
97 3396 : if (pump) {
98 3396 : rv = pump->Init(stream, streamPos, streamLen,
99 3396 : segsize, segcount, closeWhenDone);
100 3396 : if (NS_SUCCEEDED(rv)) {
101 3396 : *result = nsnull;
102 3396 : pump.swap(*result);
103 : }
104 : }
105 3396 : return rv;
106 : }
107 :
108 : struct PeekData {
109 12 : PeekData(nsInputStreamPump::PeekSegmentFun fun, void* closure)
110 12 : : mFunc(fun), mClosure(closure) {}
111 :
112 : nsInputStreamPump::PeekSegmentFun mFunc;
113 : void* mClosure;
114 : };
115 :
116 : static NS_METHOD
117 12 : CallPeekFunc(nsIInputStream *aInStream, void *aClosure,
118 : const char *aFromSegment, PRUint32 aToOffset, PRUint32 aCount,
119 : PRUint32 *aWriteCount)
120 : {
121 12 : NS_ASSERTION(aToOffset == 0, "Called more than once?");
122 12 : NS_ASSERTION(aCount > 0, "Called without data?");
123 :
124 12 : PeekData* data = static_cast<PeekData*>(aClosure);
125 : data->mFunc(data->mClosure,
126 12 : reinterpret_cast<const PRUint8*>(aFromSegment), aCount);
127 12 : return NS_BINDING_ABORTED;
128 : }
129 :
130 : nsresult
131 12 : nsInputStreamPump::PeekStream(PeekSegmentFun callback, void* closure)
132 : {
133 12 : NS_ASSERTION(mAsyncStream, "PeekStream called without stream");
134 :
135 : // See if the pipe is closed by checking the return of Available.
136 : PRUint32 dummy;
137 12 : nsresult rv = mAsyncStream->Available(&dummy);
138 12 : if (NS_FAILED(rv))
139 0 : return rv;
140 :
141 12 : PeekData data(callback, closure);
142 12 : return mAsyncStream->ReadSegments(CallPeekFunc,
143 : &data,
144 : nsIOService::gDefaultSegmentSize,
145 12 : &dummy);
146 : }
147 :
148 : nsresult
149 5378 : nsInputStreamPump::EnsureWaiting()
150 : {
151 : // no need to worry about multiple threads... an input stream pump lives
152 : // on only one thread.
153 :
154 5378 : if (!mWaiting) {
155 4753 : nsresult rv = mAsyncStream->AsyncWait(this, 0, 0, mTargetThread);
156 4753 : if (NS_FAILED(rv)) {
157 0 : NS_ERROR("AsyncWait failed");
158 0 : return rv;
159 : }
160 4753 : mWaiting = true;
161 : }
162 5378 : return NS_OK;
163 : }
164 :
165 : //-----------------------------------------------------------------------------
166 : // nsInputStreamPump::nsISupports
167 : //-----------------------------------------------------------------------------
168 :
169 : // although this class can only be accessed from one thread at a time, we do
170 : // allow its ownership to move from thread to thread, assuming the consumer
171 : // understands the limitations of this.
172 39718 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsInputStreamPump,
173 : nsIRequest,
174 : nsIInputStreamCallback,
175 : nsIInputStreamPump)
176 :
177 : //-----------------------------------------------------------------------------
178 : // nsInputStreamPump::nsIRequest
179 : //-----------------------------------------------------------------------------
180 :
181 : NS_IMETHODIMP
182 0 : nsInputStreamPump::GetName(nsACString &result)
183 : {
184 0 : result.Truncate();
185 0 : return NS_OK;
186 : }
187 :
188 : NS_IMETHODIMP
189 0 : nsInputStreamPump::IsPending(bool *result)
190 : {
191 0 : *result = (mState != STATE_IDLE);
192 0 : return NS_OK;
193 : }
194 :
195 : NS_IMETHODIMP
196 3191 : nsInputStreamPump::GetStatus(nsresult *status)
197 : {
198 3191 : *status = mStatus;
199 3191 : return NS_OK;
200 : }
201 :
202 : NS_IMETHODIMP
203 171 : nsInputStreamPump::Cancel(nsresult status)
204 : {
205 171 : LOG(("nsInputStreamPump::Cancel [this=%x status=%x]\n",
206 : this, status));
207 :
208 171 : if (NS_FAILED(mStatus)) {
209 1 : LOG((" already canceled\n"));
210 1 : return NS_OK;
211 : }
212 :
213 170 : NS_ASSERTION(NS_FAILED(status), "cancel with non-failure status code");
214 170 : mStatus = status;
215 :
216 : // close input stream
217 170 : if (mAsyncStream) {
218 170 : mAsyncStream->CloseWithStatus(status);
219 170 : if (mSuspendCount == 0)
220 43 : EnsureWaiting();
221 : // Otherwise, EnsureWaiting will be called by Resume().
222 : // Note that while suspended, OnInputStreamReady will
223 : // not do anything, and also note that calling asyncWait
224 : // on a closed stream works and will dispatch an event immediately.
225 : }
226 170 : return NS_OK;
227 : }
228 :
229 : NS_IMETHODIMP
230 778 : nsInputStreamPump::Suspend()
231 : {
232 778 : LOG(("nsInputStreamPump::Suspend [this=%x]\n", this));
233 778 : NS_ENSURE_TRUE(mState != STATE_IDLE, NS_ERROR_UNEXPECTED);
234 778 : ++mSuspendCount;
235 778 : return NS_OK;
236 : }
237 :
238 : NS_IMETHODIMP
239 778 : nsInputStreamPump::Resume()
240 : {
241 778 : LOG(("nsInputStreamPump::Resume [this=%x]\n", this));
242 778 : NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
243 778 : NS_ENSURE_TRUE(mState != STATE_IDLE, NS_ERROR_UNEXPECTED);
244 :
245 778 : if (--mSuspendCount == 0)
246 736 : EnsureWaiting();
247 778 : return NS_OK;
248 : }
249 :
250 : NS_IMETHODIMP
251 0 : nsInputStreamPump::GetLoadFlags(nsLoadFlags *aLoadFlags)
252 : {
253 0 : *aLoadFlags = mLoadFlags;
254 0 : return NS_OK;
255 : }
256 :
257 : NS_IMETHODIMP
258 0 : nsInputStreamPump::SetLoadFlags(nsLoadFlags aLoadFlags)
259 : {
260 0 : mLoadFlags = aLoadFlags;
261 0 : return NS_OK;
262 : }
263 :
264 : NS_IMETHODIMP
265 0 : nsInputStreamPump::GetLoadGroup(nsILoadGroup **aLoadGroup)
266 : {
267 0 : NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
268 0 : return NS_OK;
269 : }
270 :
271 : NS_IMETHODIMP
272 0 : nsInputStreamPump::SetLoadGroup(nsILoadGroup *aLoadGroup)
273 : {
274 0 : mLoadGroup = aLoadGroup;
275 0 : return NS_OK;
276 : }
277 :
278 : //-----------------------------------------------------------------------------
279 : // nsInputStreamPump::nsIInputStreamPump implementation
280 : //-----------------------------------------------------------------------------
281 :
282 : NS_IMETHODIMP
283 3499 : nsInputStreamPump::Init(nsIInputStream *stream,
284 : PRInt64 streamPos, PRInt64 streamLen,
285 : PRUint32 segsize, PRUint32 segcount,
286 : bool closeWhenDone)
287 : {
288 3499 : NS_ENSURE_TRUE(mState == STATE_IDLE, NS_ERROR_IN_PROGRESS);
289 :
290 3499 : mStreamOffset = PRUint64(streamPos);
291 3499 : if (PRInt64(streamLen) >= PRInt64(0))
292 0 : mStreamLength = PRUint64(streamLen);
293 3499 : mStream = stream;
294 3499 : mSegSize = segsize;
295 3499 : mSegCount = segcount;
296 3499 : mCloseWhenDone = closeWhenDone;
297 :
298 3499 : return NS_OK;
299 : }
300 :
301 : NS_IMETHODIMP
302 3487 : nsInputStreamPump::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt)
303 : {
304 3487 : NS_ENSURE_TRUE(mState == STATE_IDLE, NS_ERROR_IN_PROGRESS);
305 3487 : NS_ENSURE_ARG_POINTER(listener);
306 :
307 : //
308 : // OK, we need to use the stream transport service if
309 : //
310 : // (1) the stream is blocking
311 : // (2) the stream does not support nsIAsyncInputStream
312 : //
313 :
314 : bool nonBlocking;
315 3486 : nsresult rv = mStream->IsNonBlocking(&nonBlocking);
316 3486 : if (NS_FAILED(rv)) return rv;
317 :
318 3486 : if (nonBlocking) {
319 3223 : mAsyncStream = do_QueryInterface(mStream);
320 : //
321 : // if the stream supports nsIAsyncInputStream, and if we need to seek
322 : // to a starting offset, then we must do so here. in the non-async
323 : // stream case, the stream transport service will take care of seeking
324 : // for us.
325 : //
326 3223 : if (mAsyncStream && (mStreamOffset != LL_MAXUINT)) {
327 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
328 0 : if (seekable)
329 0 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, mStreamOffset);
330 : }
331 : }
332 :
333 3486 : if (!mAsyncStream) {
334 : // ok, let's use the stream transport service to read this stream.
335 : nsCOMPtr<nsIStreamTransportService> sts =
336 528 : do_GetService(kStreamTransportServiceCID, &rv);
337 264 : if (NS_FAILED(rv)) return rv;
338 :
339 528 : nsCOMPtr<nsITransport> transport;
340 264 : rv = sts->CreateInputTransport(mStream, mStreamOffset, mStreamLength,
341 264 : mCloseWhenDone, getter_AddRefs(transport));
342 264 : if (NS_FAILED(rv)) return rv;
343 :
344 528 : nsCOMPtr<nsIInputStream> wrapper;
345 264 : rv = transport->OpenInputStream(0, mSegSize, mSegCount, getter_AddRefs(wrapper));
346 264 : if (NS_FAILED(rv)) return rv;
347 :
348 264 : mAsyncStream = do_QueryInterface(wrapper, &rv);
349 264 : if (NS_FAILED(rv)) return rv;
350 : }
351 :
352 : // release our reference to the original stream. from this point forward,
353 : // we only reference the "stream" via mAsyncStream.
354 3486 : mStream = 0;
355 :
356 : // mStreamOffset now holds the number of bytes currently read. we use this
357 : // to enforce the mStreamLength restriction.
358 3486 : mStreamOffset = 0;
359 :
360 : // grab event queue (we must do this here by contract, since all notifications
361 : // must go to the thread which called AsyncRead)
362 3486 : mTargetThread = do_GetCurrentThread();
363 3486 : NS_ENSURE_STATE(mTargetThread);
364 :
365 3486 : rv = EnsureWaiting();
366 3486 : if (NS_FAILED(rv)) return rv;
367 :
368 3486 : if (mLoadGroup)
369 0 : mLoadGroup->AddRequest(this, nsnull);
370 :
371 3486 : mState = STATE_START;
372 3486 : mListener = listener;
373 3486 : mListenerContext = ctxt;
374 3486 : return NS_OK;
375 : }
376 :
377 : //-----------------------------------------------------------------------------
378 : // nsInputStreamPump::nsIInputStreamCallback implementation
379 : //-----------------------------------------------------------------------------
380 :
381 : NS_IMETHODIMP
382 4753 : nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream *stream)
383 : {
384 4753 : LOG(("nsInputStreamPump::OnInputStreamReady [this=%x]\n", this));
385 :
386 9506 : SAMPLE_LABEL("Input", "nsInputStreamPump::OnInputStreamReady");
387 : // this function has been called from a PLEvent, so we can safely call
388 : // any listener or progress sink methods directly from here.
389 :
390 10265 : for (;;) {
391 15018 : if (mSuspendCount || mState == STATE_IDLE) {
392 3640 : mWaiting = false;
393 3640 : break;
394 : }
395 :
396 : PRUint32 nextState;
397 11378 : switch (mState) {
398 : case STATE_START:
399 3486 : nextState = OnStateStart();
400 3486 : break;
401 : case STATE_TRANSFER:
402 4406 : nextState = OnStateTransfer();
403 4406 : break;
404 : case STATE_STOP:
405 3486 : nextState = OnStateStop();
406 3486 : break;
407 : }
408 :
409 11378 : if (mState == nextState && !mSuspendCount) {
410 1113 : NS_ASSERTION(mState == STATE_TRANSFER, "unexpected state");
411 1113 : NS_ASSERTION(NS_SUCCEEDED(mStatus), "unexpected status");
412 :
413 1113 : mWaiting = false;
414 1113 : mStatus = EnsureWaiting();
415 1113 : if (NS_SUCCEEDED(mStatus))
416 1113 : break;
417 :
418 0 : nextState = STATE_STOP;
419 : }
420 :
421 10265 : mState = nextState;
422 : }
423 4753 : return NS_OK;
424 : }
425 :
426 : PRUint32
427 3486 : nsInputStreamPump::OnStateStart()
428 : {
429 6972 : SAMPLE_LABEL("nsInputStreamPump", "OnStateStart");
430 3486 : LOG((" OnStateStart [this=%x]\n", this));
431 :
432 : nsresult rv;
433 :
434 : // need to check the reason why the stream is ready. this is required
435 : // so our listener can check our status from OnStartRequest.
436 : // XXX async streams should have a GetStatus method!
437 3486 : if (NS_SUCCEEDED(mStatus)) {
438 : PRUint32 avail;
439 3479 : rv = mAsyncStream->Available(&avail);
440 3479 : if (NS_FAILED(rv) && rv != NS_BASE_STREAM_CLOSED)
441 155 : mStatus = rv;
442 : }
443 :
444 3486 : rv = mListener->OnStartRequest(this, mListenerContext);
445 :
446 : // an error returned from OnStartRequest should cause us to abort; however,
447 : // we must not stomp on mStatus if already canceled.
448 3486 : if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus))
449 16 : mStatus = rv;
450 :
451 3486 : return NS_SUCCEEDED(mStatus) ? STATE_TRANSFER : STATE_STOP;
452 : }
453 :
454 : PRUint32
455 4406 : nsInputStreamPump::OnStateTransfer()
456 : {
457 8812 : SAMPLE_LABEL("Input", "nsInputStreamPump::OnStateTransfer");
458 4406 : LOG((" OnStateTransfer [this=%x]\n", this));
459 :
460 : // if canceled, go directly to STATE_STOP...
461 4406 : if (NS_FAILED(mStatus))
462 124 : return STATE_STOP;
463 :
464 : nsresult rv;
465 :
466 : PRUint32 avail;
467 4282 : rv = mAsyncStream->Available(&avail);
468 4282 : LOG((" Available returned [stream=%x rv=%x avail=%u]\n", mAsyncStream.get(), rv, avail));
469 :
470 4282 : if (rv == NS_BASE_STREAM_CLOSED) {
471 340 : rv = NS_OK;
472 340 : avail = 0;
473 : }
474 3942 : else if (NS_SUCCEEDED(rv) && avail) {
475 : // figure out how much data to report (XXX detect overflow??)
476 3942 : if (PRUint64(avail) + mStreamOffset > mStreamLength)
477 0 : avail = PRUint32(mStreamLength - mStreamOffset);
478 :
479 3942 : if (avail) {
480 : // we used to limit avail to 16K - we were afraid some ODA handlers
481 : // might assume they wouldn't get more than 16K at once
482 : // we're removing that limit since it speeds up local file access.
483 : // Now there's an implicit 64K limit of 4 16K segments
484 : // NOTE: ok, so the story is as follows. OnDataAvailable impls
485 : // are by contract supposed to consume exactly |avail| bytes.
486 : // however, many do not... mailnews... stream converters...
487 : // cough, cough. the input stream pump is fairly tolerant
488 : // in this regard; however, if an ODA does not consume any
489 : // data from the stream, then we could potentially end up in
490 : // an infinite loop. we do our best here to try to catch
491 : // such an error. (see bug 189672)
492 :
493 : // in most cases this QI will succeed (mAsyncStream is almost always
494 : // a nsPipeInputStream, which implements nsISeekableStream::Tell).
495 : PRInt64 offsetBefore;
496 7884 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mAsyncStream);
497 3942 : if (seekable && NS_FAILED(seekable->Tell(&offsetBefore))) {
498 0 : NS_NOTREACHED("Tell failed on readable stream");
499 0 : offsetBefore = 0;
500 : }
501 :
502 : // report the current stream offset to our listener... if we've
503 : // streamed more than PR_UINT32_MAX, then avoid overflowing the
504 : // stream offset. it's the best we can do without a 64-bit stream
505 : // listener API.
506 : PRUint32 odaOffset =
507 : mStreamOffset > PR_UINT32_MAX ?
508 3942 : PR_UINT32_MAX : PRUint32(mStreamOffset);
509 :
510 3942 : LOG((" calling OnDataAvailable [offset=%lld(%u) count=%u]\n",
511 : mStreamOffset, odaOffset, avail));
512 :
513 3942 : rv = mListener->OnDataAvailable(this, mListenerContext, mAsyncStream,
514 3942 : odaOffset, avail);
515 :
516 : // don't enter this code if ODA failed or called Cancel
517 3942 : if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(mStatus)) {
518 : // test to see if this ODA failed to consume data
519 3918 : if (seekable) {
520 : // NOTE: if Tell fails, which can happen if the stream is
521 : // now closed, then we assume that everything was read.
522 : PRInt64 offsetAfter;
523 3901 : if (NS_FAILED(seekable->Tell(&offsetAfter)))
524 2790 : offsetAfter = offsetBefore + avail;
525 3901 : if (offsetAfter > offsetBefore)
526 3901 : mStreamOffset += (offsetAfter - offsetBefore);
527 0 : else if (mSuspendCount == 0) {
528 : //
529 : // possible infinite loop if we continue pumping data!
530 : //
531 : // NOTE: although not allowed by nsIStreamListener, we
532 : // will allow the ODA impl to Suspend the pump. IMAP
533 : // does this :-(
534 : //
535 0 : NS_ERROR("OnDataAvailable implementation consumed no data");
536 0 : mStatus = NS_ERROR_UNEXPECTED;
537 : }
538 : }
539 : else
540 17 : mStreamOffset += avail; // assume ODA behaved well
541 : }
542 : }
543 : }
544 :
545 : // an error returned from Available or OnDataAvailable should cause us to
546 : // abort; however, we must not stomp on mStatus if already canceled.
547 :
548 4282 : if (NS_SUCCEEDED(mStatus)) {
549 4258 : if (NS_FAILED(rv))
550 0 : mStatus = rv;
551 4258 : else if (avail) {
552 : // if stream is now closed, advance to STATE_STOP right away.
553 : // Available may return 0 bytes available at the moment; that
554 : // would not mean that we are done.
555 : // XXX async streams should have a GetStatus method!
556 3918 : rv = mAsyncStream->Available(&avail);
557 3918 : if (NS_SUCCEEDED(rv))
558 1113 : return STATE_TRANSFER;
559 : }
560 : }
561 3169 : return STATE_STOP;
562 : }
563 :
564 : PRUint32
565 3486 : nsInputStreamPump::OnStateStop()
566 : {
567 6972 : SAMPLE_LABEL("Input", "nsInputStreamPump::OnStateTransfer");
568 3486 : LOG((" OnStateStop [this=%x status=%x]\n", this, mStatus));
569 :
570 : // if an error occurred, we must be sure to pass the error onto the async
571 : // stream. in some cases, this is redundant, but since close is idempotent,
572 : // this is OK. otherwise, be sure to honor the "close-when-done" option.
573 :
574 3486 : if (NS_FAILED(mStatus))
575 341 : mAsyncStream->CloseWithStatus(mStatus);
576 3145 : else if (mCloseWhenDone)
577 400 : mAsyncStream->Close();
578 :
579 3486 : mAsyncStream = 0;
580 3486 : mTargetThread = 0;
581 3486 : mIsPending = false;
582 :
583 3486 : mListener->OnStopRequest(this, mListenerContext, mStatus);
584 3486 : mListener = 0;
585 3486 : mListenerContext = 0;
586 :
587 3486 : if (mLoadGroup)
588 0 : mLoadGroup->RemoveRequest(this, nsnull, mStatus);
589 :
590 3486 : return STATE_IDLE;
591 : }
|