1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : *
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) 2001
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Stuart Parmenter <stuart@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "imgRequestProxy.h"
41 :
42 : #include "nsIInputStream.h"
43 : #include "nsIComponentManager.h"
44 : #include "nsIServiceManager.h"
45 : #include "nsIMultiPartChannel.h"
46 :
47 : #include "nsString.h"
48 : #include "nsXPIDLString.h"
49 : #include "nsReadableUtils.h"
50 : #include "nsCRT.h"
51 :
52 : #include "Image.h"
53 : #include "ImageErrors.h"
54 : #include "ImageLogging.h"
55 :
56 : #include "nspr.h"
57 :
58 : using namespace mozilla::image;
59 :
60 434 : NS_IMPL_ADDREF(imgRequestProxy)
61 434 : NS_IMPL_RELEASE(imgRequestProxy)
62 :
63 583 : NS_INTERFACE_MAP_BEGIN(imgRequestProxy)
64 583 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgIRequest)
65 429 : NS_INTERFACE_MAP_ENTRY(imgIRequest)
66 317 : NS_INTERFACE_MAP_ENTRY(nsIRequest)
67 313 : NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
68 313 : NS_INTERFACE_MAP_ENTRY(nsISecurityInfoProvider)
69 313 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel, TimedChannel() != nsnull)
70 313 : NS_INTERFACE_MAP_END
71 :
72 31 : imgRequestProxy::imgRequestProxy() :
73 : mOwner(nsnull),
74 : mURI(nsnull),
75 : mImage(nsnull),
76 : mPrincipal(nsnull),
77 : mListener(nsnull),
78 : mLoadFlags(nsIRequest::LOAD_NORMAL),
79 : mLockCount(0),
80 : mAnimationConsumers(0),
81 : mCanceled(false),
82 : mIsInLoadGroup(false),
83 : mListenerIsStrongRef(false),
84 : mDecodeRequested(false),
85 : mDeferNotifications(false),
86 31 : mSentStartContainer(false)
87 : {
88 : /* member initializers and constructor code */
89 :
90 31 : }
91 :
92 93 : imgRequestProxy::~imgRequestProxy()
93 : {
94 : /* destructor code */
95 31 : NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!");
96 :
97 : // Unlock the image the proper number of times if we're holding locks on it.
98 : // Note that UnlockImage() decrements mLockCount each time it's called.
99 65 : while (mLockCount)
100 3 : UnlockImage();
101 :
102 31 : ClearAnimationConsumers();
103 :
104 : // Explicitly set mListener to null to ensure that the RemoveProxy
105 : // call below can't send |this| to an arbitrary listener while |this|
106 : // is being destroyed. This is all belt-and-suspenders in view of the
107 : // above assert.
108 31 : NullOutListener();
109 :
110 31 : if (mOwner) {
111 27 : if (!mCanceled) {
112 0 : mCanceled = true;
113 :
114 : /* Call RemoveProxy with a successful status. This will keep the
115 : channel, if still downloading data, from being canceled if 'this' is
116 : the last observer. This allows the image to continue to download and
117 : be cached even if no one is using it currently.
118 :
119 : Passing false to aNotify means that we will still get
120 : OnStopRequest, if needed.
121 : */
122 0 : mOwner->RemoveProxy(this, NS_OK, false);
123 : }
124 : }
125 124 : }
126 :
127 31 : nsresult imgRequestProxy::Init(imgRequest* request, nsILoadGroup* aLoadGroup, Image* aImage,
128 : nsIURI* aURI, imgIDecoderObserver* aObserver)
129 : {
130 31 : NS_PRECONDITION(!mOwner && !mListener, "imgRequestProxy is already initialized");
131 :
132 62 : LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request", request);
133 :
134 31 : NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init");
135 :
136 31 : mOwner = request;
137 31 : mListener = aObserver;
138 : // Make sure to addref mListener before the AddProxy call below, since
139 : // that call might well want to release it if the imgRequest has
140 : // already seen OnStopRequest.
141 31 : if (mListener) {
142 29 : mListenerIsStrongRef = true;
143 29 : NS_ADDREF(mListener);
144 : }
145 31 : mLoadGroup = aLoadGroup;
146 31 : mImage = aImage;
147 31 : mURI = aURI;
148 :
149 : // Note: AddProxy won't send all the On* notifications immediately
150 31 : if (mOwner)
151 27 : mOwner->AddProxy(this);
152 :
153 31 : return NS_OK;
154 : }
155 :
156 0 : nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
157 : {
158 0 : NS_PRECONDITION(mOwner, "Cannot ChangeOwner on a proxy without an owner!");
159 :
160 : // If we're holding locks, unlock the old image.
161 : // Note that UnlockImage decrements mLockCount each time it's called.
162 0 : PRUint32 oldLockCount = mLockCount;
163 0 : while (mLockCount)
164 0 : UnlockImage();
165 :
166 : // If we're holding animation requests, undo them.
167 0 : PRUint32 oldAnimationConsumers = mAnimationConsumers;
168 0 : ClearAnimationConsumers();
169 :
170 : // Even if we are cancelled, we MUST change our image, because the image
171 : // holds our status, and the status must always be correct.
172 0 : mImage = aNewOwner->mImage;
173 :
174 : // If we were locked, apply the locks here
175 0 : for (PRUint32 i = 0; i < oldLockCount; i++)
176 0 : LockImage();
177 :
178 0 : if (mCanceled) {
179 : // If we had animation requests, restore them before exiting
180 : // (otherwise we restore them later below)
181 0 : for (PRUint32 i = 0; i < oldAnimationConsumers; i++)
182 0 : IncrementAnimationConsumers();
183 :
184 0 : return NS_OK;
185 : }
186 :
187 : // Were we decoded before?
188 0 : bool wasDecoded = false;
189 0 : if (mImage &&
190 0 : (mImage->GetStatusTracker().GetImageStatus() &
191 0 : imgIRequest::STATUS_FRAME_COMPLETE)) {
192 0 : wasDecoded = true;
193 : }
194 :
195 : // Passing false to aNotify means that mListener will still get
196 : // OnStopRequest, if needed.
197 0 : mOwner->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER, false);
198 :
199 : // If we had animation requests, restore them here. Note that we
200 : // do this *after* RemoveProxy, which clears out animation consumers
201 : // (see bug 601723).
202 0 : for (PRUint32 i = 0; i < oldAnimationConsumers; i++)
203 0 : IncrementAnimationConsumers();
204 :
205 0 : mOwner = aNewOwner;
206 :
207 0 : mOwner->AddProxy(this);
208 :
209 : // If we were decoded, or if we'd previously requested a decode, request a
210 : // decode on the new image
211 0 : if (wasDecoded || mDecodeRequested)
212 0 : mOwner->RequestDecode();
213 :
214 0 : return NS_OK;
215 : }
216 :
217 9 : void imgRequestProxy::AddToLoadGroup()
218 : {
219 9 : NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
220 :
221 9 : if (!mIsInLoadGroup && mLoadGroup) {
222 0 : mLoadGroup->AddRequest(this, nsnull);
223 0 : mIsInLoadGroup = true;
224 : }
225 9 : }
226 :
227 56 : void imgRequestProxy::RemoveFromLoadGroup(bool releaseLoadGroup)
228 : {
229 56 : if (!mIsInLoadGroup)
230 56 : return;
231 :
232 : /* calling RemoveFromLoadGroup may cause the document to finish
233 : loading, which could result in our death. We need to make sure
234 : that we stay alive long enough to fight another battle... at
235 : least until we exit this function.
236 : */
237 0 : nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
238 :
239 0 : mLoadGroup->RemoveRequest(this, nsnull, NS_OK);
240 0 : mIsInLoadGroup = false;
241 :
242 0 : if (releaseLoadGroup) {
243 : // We're done with the loadgroup, release it.
244 0 : mLoadGroup = nsnull;
245 : }
246 : }
247 :
248 :
249 : /** nsIRequest / imgIRequest methods **/
250 :
251 : /* readonly attribute wstring name; */
252 66 : NS_IMETHODIMP imgRequestProxy::GetName(nsACString &aName)
253 : {
254 66 : aName.Truncate();
255 :
256 66 : if (mURI)
257 66 : mURI->GetSpec(aName);
258 :
259 66 : return NS_OK;
260 : }
261 :
262 : /* boolean isPending (); */
263 0 : NS_IMETHODIMP imgRequestProxy::IsPending(bool *_retval)
264 : {
265 0 : return NS_ERROR_NOT_IMPLEMENTED;
266 : }
267 :
268 : /* readonly attribute nsresult status; */
269 0 : NS_IMETHODIMP imgRequestProxy::GetStatus(nsresult *aStatus)
270 : {
271 0 : return NS_ERROR_NOT_IMPLEMENTED;
272 : }
273 :
274 : /* void cancel (in nsresult status); */
275 29 : NS_IMETHODIMP imgRequestProxy::Cancel(nsresult status)
276 : {
277 29 : if (mCanceled)
278 0 : return NS_ERROR_FAILURE;
279 :
280 58 : LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
281 :
282 29 : mCanceled = true;
283 :
284 58 : nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
285 29 : return NS_DispatchToCurrentThread(ev);
286 : }
287 :
288 : void
289 29 : imgRequestProxy::DoCancel(nsresult status)
290 : {
291 : // Passing false to aNotify means that mListener will still get
292 : // OnStopRequest, if needed.
293 29 : if (mOwner)
294 27 : mOwner->RemoveProxy(this, status, false);
295 :
296 29 : NullOutListener();
297 29 : }
298 :
299 : /* void cancelAndForgetObserver (in nsresult aStatus); */
300 0 : NS_IMETHODIMP imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
301 : {
302 : // If mCanceled is true but mListener is non-null, that means
303 : // someone called Cancel() on us but the imgCancelRunnable is still
304 : // pending. We still need to null out mListener before returning
305 : // from this function in this case. That means we want to do the
306 : // RemoveProxy call right now, because we need to deliver the
307 : // onStopRequest.
308 0 : if (mCanceled && !mListener)
309 0 : return NS_ERROR_FAILURE;
310 :
311 0 : LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
312 :
313 0 : mCanceled = true;
314 :
315 : // Now cheat and make sure our removal from loadgroup happens async
316 0 : bool oldIsInLoadGroup = mIsInLoadGroup;
317 0 : mIsInLoadGroup = false;
318 :
319 : // Passing false to aNotify means that mListener will still get
320 : // OnStopRequest, if needed.
321 0 : if (mOwner)
322 0 : mOwner->RemoveProxy(this, aStatus, false);
323 :
324 0 : mIsInLoadGroup = oldIsInLoadGroup;
325 :
326 0 : if (mIsInLoadGroup) {
327 : nsCOMPtr<nsIRunnable> ev =
328 0 : NS_NewRunnableMethod(this, &imgRequestProxy::DoRemoveFromLoadGroup);
329 0 : NS_DispatchToCurrentThread(ev);
330 : }
331 :
332 0 : NullOutListener();
333 :
334 0 : return NS_OK;
335 : }
336 :
337 : /* void requestDecode (); */
338 : NS_IMETHODIMP
339 0 : imgRequestProxy::RequestDecode()
340 : {
341 0 : if (!mOwner)
342 0 : return NS_ERROR_FAILURE;
343 :
344 : // Flag this, so we know to transfer the request if our owner changes
345 0 : mDecodeRequested = true;
346 :
347 : // Forward the request
348 0 : return mOwner->RequestDecode();
349 : }
350 :
351 : /* void lockImage (); */
352 : NS_IMETHODIMP
353 3 : imgRequestProxy::LockImage()
354 : {
355 3 : mLockCount++;
356 3 : if (mImage)
357 0 : return mImage->LockImage();
358 3 : return NS_OK;
359 : }
360 :
361 : /* void unlockImage (); */
362 : NS_IMETHODIMP
363 3 : imgRequestProxy::UnlockImage()
364 : {
365 3 : NS_ABORT_IF_FALSE(mLockCount > 0, "calling unlock but no locks!");
366 :
367 3 : mLockCount--;
368 3 : if (mImage)
369 3 : return mImage->UnlockImage();
370 0 : return NS_OK;
371 : }
372 :
373 : /* void requestDiscard (); */
374 : NS_IMETHODIMP
375 0 : imgRequestProxy::RequestDiscard()
376 : {
377 0 : if (mImage) {
378 0 : return mImage->RequestDiscard();
379 : }
380 0 : return NS_OK;
381 : }
382 :
383 : NS_IMETHODIMP
384 0 : imgRequestProxy::IncrementAnimationConsumers()
385 : {
386 0 : mAnimationConsumers++;
387 0 : if (mImage)
388 0 : mImage->IncrementAnimationConsumers();
389 0 : return NS_OK;
390 : }
391 :
392 : NS_IMETHODIMP
393 0 : imgRequestProxy::DecrementAnimationConsumers()
394 : {
395 : // We may get here if some responsible code called Increment,
396 : // then called us, but we have meanwhile called ClearAnimationConsumers
397 : // because we needed to get rid of them earlier (see
398 : // imgRequest::RemoveProxy), and hence have nothing left to
399 : // decrement. (In such a case we got rid of the animation consumers
400 : // early, but not the observer.)
401 0 : if (mAnimationConsumers > 0) {
402 0 : mAnimationConsumers--;
403 0 : if (mImage)
404 0 : mImage->DecrementAnimationConsumers();
405 : }
406 0 : return NS_OK;
407 : }
408 :
409 : void
410 87 : imgRequestProxy::ClearAnimationConsumers()
411 : {
412 174 : while (mAnimationConsumers > 0)
413 0 : DecrementAnimationConsumers();
414 87 : }
415 :
416 : /* void suspend (); */
417 0 : NS_IMETHODIMP imgRequestProxy::Suspend()
418 : {
419 0 : return NS_ERROR_NOT_IMPLEMENTED;
420 : }
421 :
422 : /* void resume (); */
423 0 : NS_IMETHODIMP imgRequestProxy::Resume()
424 : {
425 0 : return NS_ERROR_NOT_IMPLEMENTED;
426 : }
427 :
428 : /* attribute nsILoadGroup loadGroup */
429 0 : NS_IMETHODIMP imgRequestProxy::GetLoadGroup(nsILoadGroup **loadGroup)
430 : {
431 0 : NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
432 0 : return NS_OK;
433 : }
434 0 : NS_IMETHODIMP imgRequestProxy::SetLoadGroup(nsILoadGroup *loadGroup)
435 : {
436 0 : mLoadGroup = loadGroup;
437 0 : return NS_OK;
438 : }
439 :
440 : /* attribute nsLoadFlags loadFlags */
441 0 : NS_IMETHODIMP imgRequestProxy::GetLoadFlags(nsLoadFlags *flags)
442 : {
443 0 : *flags = mLoadFlags;
444 0 : return NS_OK;
445 : }
446 29 : NS_IMETHODIMP imgRequestProxy::SetLoadFlags(nsLoadFlags flags)
447 : {
448 29 : mLoadFlags = flags;
449 29 : return NS_OK;
450 : }
451 :
452 : /** imgIRequest methods **/
453 :
454 : /* attribute imgIContainer image; */
455 0 : NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer * *aImage)
456 : {
457 : // It's possible that our owner has an image but hasn't notified us of it -
458 : // that'll happen if we get Canceled before the owner instantiates its image
459 : // (because Canceling unregisters us as a listener on mOwner). If we're
460 : // in that situation, just grab the image off of mOwner.
461 0 : imgIContainer* imageToReturn = mImage ? mImage : mOwner->mImage;
462 :
463 0 : if (!imageToReturn)
464 0 : return NS_ERROR_FAILURE;
465 :
466 0 : NS_ADDREF(*aImage = imageToReturn);
467 :
468 0 : return NS_OK;
469 : }
470 :
471 : /* readonly attribute unsigned long imageStatus; */
472 0 : NS_IMETHODIMP imgRequestProxy::GetImageStatus(PRUint32 *aStatus)
473 : {
474 0 : *aStatus = GetStatusTracker().GetImageStatus();
475 :
476 0 : return NS_OK;
477 : }
478 :
479 : /* readonly attribute nsIURI URI; */
480 21 : NS_IMETHODIMP imgRequestProxy::GetURI(nsIURI **aURI)
481 : {
482 21 : if (!mURI)
483 0 : return NS_ERROR_FAILURE;
484 :
485 21 : NS_ADDREF(*aURI = mURI);
486 :
487 21 : return NS_OK;
488 : }
489 :
490 : /* readonly attribute imgIDecoderObserver decoderObserver; */
491 0 : NS_IMETHODIMP imgRequestProxy::GetDecoderObserver(imgIDecoderObserver **aDecoderObserver)
492 : {
493 0 : *aDecoderObserver = mListener;
494 0 : NS_IF_ADDREF(*aDecoderObserver);
495 0 : return NS_OK;
496 : }
497 :
498 : /* readonly attribute string mimeType; */
499 0 : NS_IMETHODIMP imgRequestProxy::GetMimeType(char **aMimeType)
500 : {
501 0 : if (!mOwner)
502 0 : return NS_ERROR_FAILURE;
503 :
504 0 : const char *type = mOwner->GetMimeType();
505 0 : if (!type)
506 0 : return NS_ERROR_FAILURE;
507 :
508 0 : *aMimeType = NS_strdup(type);
509 :
510 0 : return NS_OK;
511 : }
512 :
513 14 : NS_IMETHODIMP imgRequestProxy::Clone(imgIDecoderObserver* aObserver,
514 : imgIRequest** aClone)
515 : {
516 14 : NS_PRECONDITION(aClone, "Null out param");
517 :
518 28 : LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
519 :
520 14 : *aClone = nsnull;
521 28 : nsRefPtr<imgRequestProxy> clone = new imgRequestProxy();
522 :
523 : // It is important to call |SetLoadFlags()| before calling |Init()| because
524 : // |Init()| adds the request to the loadgroup.
525 : // When a request is added to a loadgroup, its load flags are merged
526 : // with the load flags of the loadgroup.
527 : // XXXldb That's not true anymore. Stuff from imgLoader adds the
528 : // request to the loadgroup.
529 14 : clone->SetLoadFlags(mLoadFlags);
530 : nsresult rv = clone->Init(mOwner, mLoadGroup,
531 22 : mImage ? mImage : mOwner->mImage,
532 36 : mURI, aObserver);
533 14 : if (NS_FAILED(rv))
534 0 : return rv;
535 :
536 14 : clone->SetPrincipal(mPrincipal);
537 :
538 : // Assign to *aClone before calling Notify so that if the caller expects to
539 : // only be notified for requests it's already holding pointers to it won't be
540 : // surprised.
541 14 : NS_ADDREF(*aClone = clone);
542 :
543 : // This is wrong!!! We need to notify asynchronously, but there's code that
544 : // assumes that we don't. This will be fixed in bug 580466.
545 14 : clone->SyncNotifyListener();
546 :
547 14 : return NS_OK;
548 : }
549 :
550 : /* readonly attribute nsIPrincipal imagePrincipal; */
551 0 : NS_IMETHODIMP imgRequestProxy::GetImagePrincipal(nsIPrincipal **aPrincipal)
552 : {
553 0 : if (!mPrincipal)
554 0 : return NS_ERROR_FAILURE;
555 :
556 0 : NS_ADDREF(*aPrincipal = mPrincipal);
557 :
558 0 : return NS_OK;
559 : }
560 :
561 : /* readonly attribute PRInt32 CORSMode; */
562 0 : NS_IMETHODIMP imgRequestProxy::GetCORSMode(PRInt32* aCorsMode)
563 : {
564 0 : if (!mOwner)
565 0 : return NS_ERROR_FAILURE;
566 :
567 0 : *aCorsMode = mOwner->GetCORSMode();
568 :
569 0 : return NS_OK;
570 : }
571 :
572 : /** nsISupportsPriority methods **/
573 :
574 0 : NS_IMETHODIMP imgRequestProxy::GetPriority(PRInt32 *priority)
575 : {
576 0 : NS_ENSURE_STATE(mOwner);
577 0 : *priority = mOwner->Priority();
578 0 : return NS_OK;
579 : }
580 :
581 0 : NS_IMETHODIMP imgRequestProxy::SetPriority(PRInt32 priority)
582 : {
583 0 : NS_ENSURE_STATE(mOwner && !mCanceled);
584 0 : mOwner->AdjustPriority(this, priority - mOwner->Priority());
585 0 : return NS_OK;
586 : }
587 :
588 0 : NS_IMETHODIMP imgRequestProxy::AdjustPriority(PRInt32 priority)
589 : {
590 0 : NS_ENSURE_STATE(mOwner && !mCanceled);
591 0 : mOwner->AdjustPriority(this, priority);
592 0 : return NS_OK;
593 : }
594 :
595 : /** nsISecurityInfoProvider methods **/
596 :
597 0 : NS_IMETHODIMP imgRequestProxy::GetSecurityInfo(nsISupports** _retval)
598 : {
599 0 : if (mOwner)
600 0 : return mOwner->GetSecurityInfo(_retval);
601 :
602 0 : *_retval = nsnull;
603 0 : return NS_OK;
604 : }
605 :
606 0 : NS_IMETHODIMP imgRequestProxy::GetHasTransferredData(bool* hasData)
607 : {
608 0 : if (mOwner) {
609 0 : *hasData = mOwner->HasTransferredData();
610 : } else {
611 : // The safe thing to do is to claim we have data
612 0 : *hasData = true;
613 : }
614 0 : return NS_OK;
615 : }
616 :
617 : /** imgIContainerObserver methods **/
618 :
619 0 : void imgRequestProxy::FrameChanged(imgIContainer *container,
620 : const nsIntRect *dirtyRect)
621 : {
622 0 : LOG_FUNC(gImgLog, "imgRequestProxy::FrameChanged");
623 :
624 0 : if (mListener && !mCanceled) {
625 : // Hold a ref to the listener while we call it, just in case.
626 0 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
627 0 : mListener->FrameChanged(this, container, dirtyRect);
628 : }
629 0 : }
630 :
631 : /** imgIDecoderObserver methods **/
632 :
633 12 : void imgRequestProxy::OnStartDecode()
634 : {
635 12 : LOG_FUNC(gImgLog, "imgRequestProxy::OnStartDecode");
636 :
637 12 : if (mListener && !mCanceled) {
638 : // Hold a ref to the listener while we call it, just in case.
639 4 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
640 2 : mListener->OnStartDecode(this);
641 : }
642 12 : }
643 :
644 30 : void imgRequestProxy::OnStartContainer(imgIContainer *image)
645 : {
646 30 : LOG_FUNC(gImgLog, "imgRequestProxy::OnStartContainer");
647 :
648 30 : if (mListener && !mCanceled && !mSentStartContainer) {
649 : // Hold a ref to the listener while we call it, just in case.
650 40 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
651 20 : mListener->OnStartContainer(this, image);
652 20 : mSentStartContainer = true;
653 : }
654 30 : }
655 :
656 22 : void imgRequestProxy::OnStartFrame(PRUint32 frame)
657 : {
658 22 : LOG_FUNC(gImgLog, "imgRequestProxy::OnStartFrame");
659 :
660 22 : if (mListener && !mCanceled) {
661 : // Hold a ref to the listener while we call it, just in case.
662 4 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
663 2 : mListener->OnStartFrame(this, frame);
664 : }
665 22 : }
666 :
667 12 : void imgRequestProxy::OnDataAvailable(bool aCurrentFrame, const nsIntRect * rect)
668 : {
669 12 : LOG_FUNC(gImgLog, "imgRequestProxy::OnDataAvailable");
670 :
671 12 : if (mListener && !mCanceled) {
672 : // Hold a ref to the listener while we call it, just in case.
673 4 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
674 2 : mListener->OnDataAvailable(this, aCurrentFrame, rect);
675 : }
676 12 : }
677 :
678 22 : void imgRequestProxy::OnStopFrame(PRUint32 frame)
679 : {
680 22 : LOG_FUNC(gImgLog, "imgRequestProxy::OnStopFrame");
681 :
682 22 : if (mListener && !mCanceled) {
683 : // Hold a ref to the listener while we call it, just in case.
684 4 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
685 2 : mListener->OnStopFrame(this, frame);
686 : }
687 22 : }
688 :
689 12 : void imgRequestProxy::OnStopContainer(imgIContainer *image)
690 : {
691 12 : LOG_FUNC(gImgLog, "imgRequestProxy::OnStopContainer");
692 :
693 12 : if (mListener && !mCanceled) {
694 : // Hold a ref to the listener while we call it, just in case.
695 4 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
696 2 : mListener->OnStopContainer(this, image);
697 : }
698 12 : }
699 :
700 29 : void imgRequestProxy::OnStopDecode(nsresult status, const PRUnichar *statusArg)
701 : {
702 29 : LOG_FUNC(gImgLog, "imgRequestProxy::OnStopDecode");
703 :
704 29 : if (mListener && !mCanceled) {
705 : // Hold a ref to the listener while we call it, just in case.
706 58 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
707 29 : mListener->OnStopDecode(this, status, statusArg);
708 : }
709 29 : }
710 :
711 0 : void imgRequestProxy::OnDiscard()
712 : {
713 0 : LOG_FUNC(gImgLog, "imgRequestProxy::OnDiscard");
714 :
715 0 : if (mListener && !mCanceled) {
716 : // Hold a ref to the listener while we call it, just in case.
717 0 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
718 0 : mListener->OnDiscard(this);
719 : }
720 0 : }
721 :
722 5 : void imgRequestProxy::OnImageIsAnimated()
723 : {
724 5 : LOG_FUNC(gImgLog, "imgRequestProxy::OnImageIsAnimated");
725 5 : if (mListener && !mCanceled) {
726 : // Hold a ref to the listener while we call it, just in case.
727 0 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
728 0 : mListener->OnImageIsAnimated(this);
729 : }
730 5 : }
731 :
732 37 : void imgRequestProxy::OnStartRequest()
733 : {
734 : #ifdef PR_LOGGING
735 74 : nsCAutoString name;
736 37 : GetName(name);
737 37 : LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStartRequest", "name", name.get());
738 : #endif
739 :
740 : // Notify even if mCanceled, since OnStartRequest is guaranteed by the
741 : // nsIStreamListener contract so it makes sense to do the same here.
742 37 : if (mListener) {
743 : // Hold a ref to the listener while we call it, just in case.
744 74 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
745 37 : mListener->OnStartRequest(this);
746 : }
747 37 : }
748 :
749 29 : void imgRequestProxy::OnStopRequest(bool lastPart)
750 : {
751 : #ifdef PR_LOGGING
752 58 : nsCAutoString name;
753 29 : GetName(name);
754 29 : LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStopRequest", "name", name.get());
755 : #endif
756 : // There's all sorts of stuff here that could kill us (the OnStopRequest call
757 : // on the listener, the removal from the loadgroup, the release of the
758 : // listener, etc). Don't let them do it.
759 58 : nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
760 :
761 29 : if (mListener) {
762 : // Hold a ref to the listener while we call it, just in case.
763 58 : nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
764 29 : mListener->OnStopRequest(this, lastPart);
765 : }
766 :
767 : // If we're expecting more data from a multipart channel, re-add ourself
768 : // to the loadgroup so that the document doesn't lose track of the load.
769 : // If the request is already a background request and there's more data
770 : // coming, we can just leave the request in the loadgroup as-is.
771 29 : if (lastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
772 29 : RemoveFromLoadGroup(lastPart);
773 : // More data is coming, so change the request to be a background request
774 : // and put it back in the loadgroup.
775 29 : if (!lastPart) {
776 0 : mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
777 0 : AddToLoadGroup();
778 : }
779 : }
780 :
781 29 : if (mListenerIsStrongRef) {
782 29 : NS_PRECONDITION(mListener, "How did that happen?");
783 : // Drop our strong ref to the listener now that we're done with
784 : // everything. Note that this can cancel us and other fun things
785 : // like that. Don't add anything in this method after this point.
786 29 : imgIDecoderObserver* obs = mListener;
787 29 : mListenerIsStrongRef = false;
788 29 : NS_RELEASE(obs);
789 : }
790 29 : }
791 :
792 60 : void imgRequestProxy::NullOutListener()
793 : {
794 : // If we have animation consumers, then they don't matter anymore
795 60 : if (mListener)
796 29 : ClearAnimationConsumers();
797 :
798 60 : if (mListenerIsStrongRef) {
799 : // Releasing could do weird reentery stuff, so just play it super-safe
800 0 : nsCOMPtr<imgIDecoderObserver> obs;
801 0 : obs.swap(mListener);
802 0 : mListenerIsStrongRef = false;
803 : } else {
804 60 : mListener = nsnull;
805 : }
806 60 : }
807 :
808 : NS_IMETHODIMP
809 3 : imgRequestProxy::GetStaticRequest(imgIRequest** aReturn)
810 : {
811 3 : *aReturn = nsnull;
812 :
813 : bool animated;
814 3 : if (!mImage || (NS_SUCCEEDED(mImage->GetAnimated(&animated)) && !animated)) {
815 : // Early exit - we're not animated, so we don't have to do anything.
816 0 : NS_ADDREF(*aReturn = this);
817 0 : return NS_OK;
818 : }
819 :
820 : // We are animated. We need to extract the current frame from this image.
821 3 : PRInt32 w = 0;
822 3 : PRInt32 h = 0;
823 3 : mImage->GetWidth(&w);
824 3 : mImage->GetHeight(&h);
825 3 : nsIntRect rect(0, 0, w, h);
826 6 : nsCOMPtr<imgIContainer> currentFrame;
827 3 : nsresult rv = mImage->ExtractFrame(imgIContainer::FRAME_CURRENT, rect,
828 : imgIContainer::FLAG_SYNC_DECODE,
829 3 : getter_AddRefs(currentFrame));
830 3 : if (NS_FAILED(rv))
831 1 : return rv;
832 :
833 4 : nsRefPtr<Image> frame = static_cast<Image*>(currentFrame.get());
834 :
835 : // Create a static imgRequestProxy with our new extracted frame.
836 4 : nsRefPtr<imgRequestProxy> req = new imgRequestProxy();
837 2 : req->Init(nsnull, nsnull, frame, mURI, nsnull);
838 2 : req->SetPrincipal(mPrincipal);
839 :
840 2 : NS_ADDREF(*aReturn = req);
841 :
842 2 : return NS_OK;
843 : }
844 :
845 62 : void imgRequestProxy::SetPrincipal(nsIPrincipal *aPrincipal)
846 : {
847 62 : mPrincipal = aPrincipal;
848 62 : }
849 :
850 7 : void imgRequestProxy::NotifyListener()
851 : {
852 : // It would be nice to notify the observer directly in the status tracker
853 : // instead of through the proxy, but there are several places we do extra
854 : // processing when we receive notifications (like OnStopRequest()), and we
855 : // need to check mCanceled everywhere too.
856 :
857 7 : if (mOwner) {
858 : // Send the notifications to our listener asynchronously.
859 7 : GetStatusTracker().Notify(mOwner, this);
860 : } else {
861 : // We don't have an imgRequest, so we can only notify the clone of our
862 : // current state, but we still have to do that asynchronously.
863 0 : NS_ABORT_IF_FALSE(mImage,
864 : "if we have no imgRequest, we should have an Image");
865 0 : mImage->GetStatusTracker().NotifyCurrentState(this);
866 : }
867 7 : }
868 :
869 14 : void imgRequestProxy::SyncNotifyListener()
870 : {
871 : // It would be nice to notify the observer directly in the status tracker
872 : // instead of through the proxy, but there are several places we do extra
873 : // processing when we receive notifications (like OnStopRequest()), and we
874 : // need to check mCanceled everywhere too.
875 :
876 14 : GetStatusTracker().SyncNotify(this);
877 14 : }
878 :
879 : void
880 19 : imgRequestProxy::SetImage(Image* aImage)
881 : {
882 19 : NS_ABORT_IF_FALSE(aImage, "Setting null image");
883 19 : NS_ABORT_IF_FALSE(!mImage, "Setting image when we already have one");
884 :
885 19 : mImage = aImage;
886 :
887 : // Apply any locks we have
888 22 : for (PRUint32 i = 0; i < mLockCount; ++i)
889 3 : mImage->LockImage();
890 :
891 : // Apply any animation consumers we have
892 19 : for (PRUint32 i = 0; i < mAnimationConsumers; i++)
893 0 : mImage->IncrementAnimationConsumers();
894 19 : }
895 :
896 : imgStatusTracker&
897 21 : imgRequestProxy::GetStatusTracker()
898 : {
899 : // NOTE: It's possible that our mOwner has an Image that it didn't notify
900 : // us about, if we were Canceled before its Image was constructed.
901 : // (Canceling removes us as an observer, so mOwner has no way to notify us).
902 : // That's why this method uses mOwner->GetStatusTracker() instead of just
903 : // mOwner->mStatusTracker -- we might have a null mImage and yet have an
904 : // mOwner with a non-null mImage (and a null mStatusTracker pointer).
905 21 : return mImage ? mImage->GetStatusTracker() : mOwner->GetStatusTracker();
906 : }
|