Bumping manifests a=b2g-bump
[gecko.git] / image / src / imgRequestProxy.cpp
blob3ced4f8e9440572a3050bfe2b13cbea4b7c0e557
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ImageLogging.h"
8 #include "imgRequestProxy.h"
9 #include "imgIOnloadBlocker.h"
11 #include "Image.h"
12 #include "ImageOps.h"
13 #include "nsError.h"
14 #include "nsCRTGlue.h"
15 #include "imgINotificationObserver.h"
16 #include "nsNetUtil.h"
18 using namespace mozilla::image;
20 // The split of imgRequestProxy and imgRequestProxyStatic means that
21 // certain overridden functions need to be usable in the destructor.
22 // Since virtual functions can't be used in that way, this class
23 // provides a behavioural trait for each class to use instead.
24 class ProxyBehaviour
26 public:
27 virtual ~ProxyBehaviour() {}
29 virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0;
30 virtual bool HasImage() const = 0;
31 virtual already_AddRefed<ProgressTracker> GetProgressTracker() const = 0;
32 virtual imgRequest* GetOwner() const = 0;
33 virtual void SetOwner(imgRequest* aOwner) = 0;
36 class RequestBehaviour : public ProxyBehaviour
38 public:
39 RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {}
41 virtual already_AddRefed<mozilla::image::Image> GetImage() const MOZ_OVERRIDE;
42 virtual bool HasImage() const MOZ_OVERRIDE;
43 virtual already_AddRefed<ProgressTracker> GetProgressTracker() const MOZ_OVERRIDE;
45 virtual imgRequest* GetOwner() const MOZ_OVERRIDE {
46 return mOwner;
49 virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE {
50 mOwner = aOwner;
52 if (mOwner) {
53 nsRefPtr<ProgressTracker> ownerProgressTracker = GetProgressTracker();
54 mOwnerHasImage = ownerProgressTracker && ownerProgressTracker->HasImage();
55 } else {
56 mOwnerHasImage = false;
60 private:
61 // We maintain the following invariant:
62 // The proxy is registered at most with a single imgRequest as an observer,
63 // and whenever it is, mOwner points to that object. This helps ensure that
64 // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
65 // from whatever request it was registered with (if any). This, in turn,
66 // means that imgRequest::mObservers will not have any stale pointers in it.
67 nsRefPtr<imgRequest> mOwner;
69 bool mOwnerHasImage;
72 already_AddRefed<mozilla::image::Image>
73 RequestBehaviour::GetImage() const
75 if (!mOwnerHasImage)
76 return nullptr;
77 nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
78 return progressTracker->GetImage();
81 already_AddRefed<ProgressTracker>
82 RequestBehaviour::GetProgressTracker() const
84 // NOTE: It's possible that our mOwner has an Image that it didn't notify
85 // us about, if we were Canceled before its Image was constructed.
86 // (Canceling removes us as an observer, so mOwner has no way to notify us).
87 // That's why this method uses mOwner->GetProgressTracker() instead of just
88 // mOwner->mProgressTracker -- we might have a null mImage and yet have an
89 // mOwner with a non-null mImage (and a null mProgressTracker pointer).
90 return mOwner->GetProgressTracker();
93 NS_IMPL_ADDREF(imgRequestProxy)
94 NS_IMPL_RELEASE(imgRequestProxy)
96 NS_INTERFACE_MAP_BEGIN(imgRequestProxy)
97 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgIRequest)
98 NS_INTERFACE_MAP_ENTRY(imgIRequest)
99 NS_INTERFACE_MAP_ENTRY(nsIRequest)
100 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
101 NS_INTERFACE_MAP_ENTRY(nsISecurityInfoProvider)
102 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel, TimedChannel() != nullptr)
103 NS_INTERFACE_MAP_END
105 imgRequestProxy::imgRequestProxy() :
106 mBehaviour(new RequestBehaviour),
107 mURI(nullptr),
108 mListener(nullptr),
109 mLoadFlags(nsIRequest::LOAD_NORMAL),
110 mLockCount(0),
111 mAnimationConsumers(0),
112 mCanceled(false),
113 mIsInLoadGroup(false),
114 mListenerIsStrongRef(false),
115 mDecodeRequested(false),
116 mDeferNotifications(false)
118 /* member initializers and constructor code */
122 imgRequestProxy::~imgRequestProxy()
124 /* destructor code */
125 NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!");
127 // Unlock the image the proper number of times if we're holding locks on it.
128 // Note that UnlockImage() decrements mLockCount each time it's called.
129 while (mLockCount)
130 UnlockImage();
132 ClearAnimationConsumers();
134 // Explicitly set mListener to null to ensure that the RemoveProxy
135 // call below can't send |this| to an arbitrary listener while |this|
136 // is being destroyed. This is all belt-and-suspenders in view of the
137 // above assert.
138 NullOutListener();
140 if (GetOwner()) {
141 /* Call RemoveProxy with a successful status. This will keep the
142 channel, if still downloading data, from being canceled if 'this' is
143 the last observer. This allows the image to continue to download and
144 be cached even if no one is using it currently.
146 mCanceled = true;
147 GetOwner()->RemoveProxy(this, NS_OK);
151 nsresult imgRequestProxy::Init(imgRequest* aOwner,
152 nsILoadGroup* aLoadGroup,
153 ImageURL* aURI,
154 imgINotificationObserver* aObserver)
156 NS_PRECONDITION(!GetOwner() && !mListener, "imgRequestProxy is already initialized");
158 LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request", aOwner);
160 NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init");
162 mBehaviour->SetOwner(aOwner);
163 mListener = aObserver;
164 // Make sure to addref mListener before the AddProxy call below, since
165 // that call might well want to release it if the imgRequest has
166 // already seen OnStopRequest.
167 if (mListener) {
168 mListenerIsStrongRef = true;
169 NS_ADDREF(mListener);
171 mLoadGroup = aLoadGroup;
172 mURI = aURI;
174 // Note: AddProxy won't send all the On* notifications immediately
175 if (GetOwner())
176 GetOwner()->AddProxy(this);
178 return NS_OK;
181 nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
183 NS_PRECONDITION(GetOwner(), "Cannot ChangeOwner on a proxy without an owner!");
185 if (mCanceled) {
186 // Ensure that this proxy has received all notifications to date before
187 // we clean it up when removing it from the old owner below.
188 SyncNotifyListener();
191 // If we're holding locks, unlock the old image.
192 // Note that UnlockImage decrements mLockCount each time it's called.
193 uint32_t oldLockCount = mLockCount;
194 while (mLockCount)
195 UnlockImage();
197 // If we're holding animation requests, undo them.
198 uint32_t oldAnimationConsumers = mAnimationConsumers;
199 ClearAnimationConsumers();
201 // Were we decoded before?
202 bool wasDecoded = false;
203 nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
204 if (progressTracker->HasImage() &&
205 progressTracker->GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE) {
206 wasDecoded = true;
209 GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
211 mBehaviour->SetOwner(aNewOwner);
213 // If we were locked, apply the locks here
214 for (uint32_t i = 0; i < oldLockCount; i++)
215 LockImage();
217 // If we had animation requests, restore them here. Note that we
218 // do this *after* RemoveProxy, which clears out animation consumers
219 // (see bug 601723).
220 for (uint32_t i = 0; i < oldAnimationConsumers; i++)
221 IncrementAnimationConsumers();
223 GetOwner()->AddProxy(this);
225 // If we were decoded, or if we'd previously requested a decode, request a
226 // decode on the new image
227 if (wasDecoded || mDecodeRequested)
228 GetOwner()->StartDecoding();
230 return NS_OK;
233 void imgRequestProxy::AddToLoadGroup()
235 NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
237 if (!mIsInLoadGroup && mLoadGroup) {
238 mLoadGroup->AddRequest(this, nullptr);
239 mIsInLoadGroup = true;
243 void imgRequestProxy::RemoveFromLoadGroup(bool releaseLoadGroup)
245 if (!mIsInLoadGroup)
246 return;
248 /* calling RemoveFromLoadGroup may cause the document to finish
249 loading, which could result in our death. We need to make sure
250 that we stay alive long enough to fight another battle... at
251 least until we exit this function.
253 nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
255 mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
256 mIsInLoadGroup = false;
258 if (releaseLoadGroup) {
259 // We're done with the loadgroup, release it.
260 mLoadGroup = nullptr;
265 /** nsIRequest / imgIRequest methods **/
267 /* readonly attribute wstring name; */
268 NS_IMETHODIMP imgRequestProxy::GetName(nsACString &aName)
270 aName.Truncate();
272 if (mURI)
273 mURI->GetSpec(aName);
275 return NS_OK;
278 /* boolean isPending (); */
279 NS_IMETHODIMP imgRequestProxy::IsPending(bool *_retval)
281 return NS_ERROR_NOT_IMPLEMENTED;
284 /* readonly attribute nsresult status; */
285 NS_IMETHODIMP imgRequestProxy::GetStatus(nsresult *aStatus)
287 return NS_ERROR_NOT_IMPLEMENTED;
290 /* void cancel (in nsresult status); */
291 NS_IMETHODIMP imgRequestProxy::Cancel(nsresult status)
293 if (mCanceled)
294 return NS_ERROR_FAILURE;
296 LOG_SCOPE(GetImgLog(), "imgRequestProxy::Cancel");
298 mCanceled = true;
300 nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
301 return NS_DispatchToCurrentThread(ev);
304 void
305 imgRequestProxy::DoCancel(nsresult status)
307 if (GetOwner()) {
308 GetOwner()->RemoveProxy(this, status);
311 NullOutListener();
314 /* void cancelAndForgetObserver (in nsresult aStatus); */
315 NS_IMETHODIMP imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
317 // If mCanceled is true but mListener is non-null, that means
318 // someone called Cancel() on us but the imgCancelRunnable is still
319 // pending. We still need to null out mListener before returning
320 // from this function in this case. That means we want to do the
321 // RemoveProxy call right now, because we need to deliver the
322 // onStopRequest.
323 if (mCanceled && !mListener)
324 return NS_ERROR_FAILURE;
326 LOG_SCOPE(GetImgLog(), "imgRequestProxy::CancelAndForgetObserver");
328 mCanceled = true;
330 // Now cheat and make sure our removal from loadgroup happens async
331 bool oldIsInLoadGroup = mIsInLoadGroup;
332 mIsInLoadGroup = false;
334 if (GetOwner()) {
335 GetOwner()->RemoveProxy(this, aStatus);
338 mIsInLoadGroup = oldIsInLoadGroup;
340 if (mIsInLoadGroup) {
341 nsCOMPtr<nsIRunnable> ev =
342 NS_NewRunnableMethod(this, &imgRequestProxy::DoRemoveFromLoadGroup);
343 NS_DispatchToCurrentThread(ev);
346 NullOutListener();
348 return NS_OK;
351 /* void startDecode (); */
352 NS_IMETHODIMP
353 imgRequestProxy::StartDecoding()
355 if (!GetOwner())
356 return NS_ERROR_FAILURE;
358 // Flag this, so we know to transfer the request if our owner changes
359 mDecodeRequested = true;
361 // Forward the request
362 return GetOwner()->StartDecoding();
365 /* void requestDecode (); */
366 NS_IMETHODIMP
367 imgRequestProxy::RequestDecode()
369 if (!GetOwner())
370 return NS_ERROR_FAILURE;
372 // Flag this, so we know to transfer the request if our owner changes
373 mDecodeRequested = true;
375 // Forward the request
376 return GetOwner()->RequestDecode();
380 /* void lockImage (); */
381 NS_IMETHODIMP
382 imgRequestProxy::LockImage()
384 mLockCount++;
385 nsRefPtr<Image> image = GetImage();
386 if (image)
387 return image->LockImage();
388 return NS_OK;
391 /* void unlockImage (); */
392 NS_IMETHODIMP
393 imgRequestProxy::UnlockImage()
395 NS_ABORT_IF_FALSE(mLockCount > 0, "calling unlock but no locks!");
397 mLockCount--;
398 nsRefPtr<Image> image = GetImage();
399 if (image)
400 return image->UnlockImage();
401 return NS_OK;
404 /* void requestDiscard (); */
405 NS_IMETHODIMP
406 imgRequestProxy::RequestDiscard()
408 nsRefPtr<Image> image = GetImage();
409 if (image)
410 return image->RequestDiscard();
411 return NS_OK;
414 NS_IMETHODIMP
415 imgRequestProxy::IncrementAnimationConsumers()
417 mAnimationConsumers++;
418 nsRefPtr<Image> image = GetImage();
419 if (image)
420 image->IncrementAnimationConsumers();
421 return NS_OK;
424 NS_IMETHODIMP
425 imgRequestProxy::DecrementAnimationConsumers()
427 // We may get here if some responsible code called Increment,
428 // then called us, but we have meanwhile called ClearAnimationConsumers
429 // because we needed to get rid of them earlier (see
430 // imgRequest::RemoveProxy), and hence have nothing left to
431 // decrement. (In such a case we got rid of the animation consumers
432 // early, but not the observer.)
433 if (mAnimationConsumers > 0) {
434 mAnimationConsumers--;
435 nsRefPtr<Image> image = GetImage();
436 if (image)
437 image->DecrementAnimationConsumers();
439 return NS_OK;
442 void
443 imgRequestProxy::ClearAnimationConsumers()
445 while (mAnimationConsumers > 0)
446 DecrementAnimationConsumers();
449 /* void suspend (); */
450 NS_IMETHODIMP imgRequestProxy::Suspend()
452 return NS_ERROR_NOT_IMPLEMENTED;
455 /* void resume (); */
456 NS_IMETHODIMP imgRequestProxy::Resume()
458 return NS_ERROR_NOT_IMPLEMENTED;
461 /* attribute nsILoadGroup loadGroup */
462 NS_IMETHODIMP imgRequestProxy::GetLoadGroup(nsILoadGroup **loadGroup)
464 NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
465 return NS_OK;
467 NS_IMETHODIMP imgRequestProxy::SetLoadGroup(nsILoadGroup *loadGroup)
469 mLoadGroup = loadGroup;
470 return NS_OK;
473 /* attribute nsLoadFlags loadFlags */
474 NS_IMETHODIMP imgRequestProxy::GetLoadFlags(nsLoadFlags *flags)
476 *flags = mLoadFlags;
477 return NS_OK;
479 NS_IMETHODIMP imgRequestProxy::SetLoadFlags(nsLoadFlags flags)
481 mLoadFlags = flags;
482 return NS_OK;
485 /** imgIRequest methods **/
487 /* attribute imgIContainer image; */
488 NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer **aImage)
490 NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
491 // It's possible that our owner has an image but hasn't notified us of it -
492 // that'll happen if we get Canceled before the owner instantiates its image
493 // (because Canceling unregisters us as a listener on mOwner). If we're
494 // in that situation, just grab the image off of mOwner.
495 nsRefPtr<Image> image = GetImage();
496 nsCOMPtr<imgIContainer> imageToReturn;
497 if (image)
498 imageToReturn = do_QueryInterface(image);
499 if (!imageToReturn && GetOwner()) {
500 imageToReturn = GetOwner()->GetImage();
503 if (!imageToReturn)
504 return NS_ERROR_FAILURE;
506 imageToReturn.swap(*aImage);
508 return NS_OK;
511 /* readonly attribute unsigned long imageStatus; */
512 NS_IMETHODIMP imgRequestProxy::GetImageStatus(uint32_t *aStatus)
514 nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
515 *aStatus = progressTracker->GetImageStatus();
517 return NS_OK;
520 /* readonly attribute nresult imageErrorCode; */
521 NS_IMETHODIMP imgRequestProxy::GetImageErrorCode(nsresult *aStatus)
523 if (!GetOwner())
524 return NS_ERROR_FAILURE;
526 *aStatus = GetOwner()->GetImageErrorCode();
528 return NS_OK;
531 /* readonly attribute nsIURI URI; */
532 NS_IMETHODIMP imgRequestProxy::GetURI(nsIURI **aURI)
534 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
535 nsCOMPtr<nsIURI> uri = mURI->ToIURI();
536 uri.forget(aURI);
537 return NS_OK;
540 nsresult imgRequestProxy::GetCurrentURI(nsIURI **aURI)
542 if (!GetOwner())
543 return NS_ERROR_FAILURE;
545 return GetOwner()->GetCurrentURI(aURI);
548 nsresult imgRequestProxy::GetURI(ImageURL **aURI)
550 if (!mURI)
551 return NS_ERROR_FAILURE;
553 NS_ADDREF(*aURI = mURI);
555 return NS_OK;
558 /* readonly attribute imgINotificationObserver notificationObserver; */
559 NS_IMETHODIMP imgRequestProxy::GetNotificationObserver(imgINotificationObserver **aObserver)
561 *aObserver = mListener;
562 NS_IF_ADDREF(*aObserver);
563 return NS_OK;
566 /* readonly attribute string mimeType; */
567 NS_IMETHODIMP imgRequestProxy::GetMimeType(char **aMimeType)
569 if (!GetOwner())
570 return NS_ERROR_FAILURE;
572 const char *type = GetOwner()->GetMimeType();
573 if (!type)
574 return NS_ERROR_FAILURE;
576 *aMimeType = NS_strdup(type);
578 return NS_OK;
581 static imgRequestProxy* NewProxy(imgRequestProxy* /*aThis*/)
583 return new imgRequestProxy();
586 imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis)
588 nsCOMPtr<nsIPrincipal> currentPrincipal;
589 aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal));
590 nsRefPtr<Image> image = aThis->GetImage();
591 return new imgRequestProxyStatic(image, currentPrincipal);
594 NS_IMETHODIMP imgRequestProxy::Clone(imgINotificationObserver* aObserver,
595 imgIRequest** aClone)
597 nsresult result;
598 imgRequestProxy* proxy;
599 result = Clone(aObserver, &proxy);
600 *aClone = proxy;
601 return result;
604 nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
605 imgRequestProxy** aClone)
607 return PerformClone(aObserver, NewProxy, aClone);
610 nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
611 imgRequestProxy* (aAllocFn)(imgRequestProxy*),
612 imgRequestProxy** aClone)
614 NS_PRECONDITION(aClone, "Null out param");
616 LOG_SCOPE(GetImgLog(), "imgRequestProxy::Clone");
618 *aClone = nullptr;
619 nsRefPtr<imgRequestProxy> clone = aAllocFn(this);
621 // It is important to call |SetLoadFlags()| before calling |Init()| because
622 // |Init()| adds the request to the loadgroup.
623 // When a request is added to a loadgroup, its load flags are merged
624 // with the load flags of the loadgroup.
625 // XXXldb That's not true anymore. Stuff from imgLoader adds the
626 // request to the loadgroup.
627 clone->SetLoadFlags(mLoadFlags);
628 nsresult rv = clone->Init(mBehaviour->GetOwner(), mLoadGroup, mURI, aObserver);
629 if (NS_FAILED(rv))
630 return rv;
632 // Assign to *aClone before calling Notify so that if the caller expects to
633 // only be notified for requests it's already holding pointers to it won't be
634 // surprised.
635 NS_ADDREF(*aClone = clone);
637 // This is wrong!!! We need to notify asynchronously, but there's code that
638 // assumes that we don't. This will be fixed in bug 580466.
639 clone->SyncNotifyListener();
641 return NS_OK;
644 /* readonly attribute nsIPrincipal imagePrincipal; */
645 NS_IMETHODIMP imgRequestProxy::GetImagePrincipal(nsIPrincipal **aPrincipal)
647 if (!GetOwner())
648 return NS_ERROR_FAILURE;
650 NS_ADDREF(*aPrincipal = GetOwner()->GetPrincipal());
651 return NS_OK;
654 /* readonly attribute bool multipart; */
655 NS_IMETHODIMP imgRequestProxy::GetMultipart(bool *aMultipart)
657 if (!GetOwner())
658 return NS_ERROR_FAILURE;
660 *aMultipart = GetOwner()->GetMultipart();
662 return NS_OK;
665 /* readonly attribute int32_t CORSMode; */
666 NS_IMETHODIMP imgRequestProxy::GetCORSMode(int32_t* aCorsMode)
668 if (!GetOwner())
669 return NS_ERROR_FAILURE;
671 *aCorsMode = GetOwner()->GetCORSMode();
673 return NS_OK;
676 /** nsISupportsPriority methods **/
678 NS_IMETHODIMP imgRequestProxy::GetPriority(int32_t *priority)
680 NS_ENSURE_STATE(GetOwner());
681 *priority = GetOwner()->Priority();
682 return NS_OK;
685 NS_IMETHODIMP imgRequestProxy::SetPriority(int32_t priority)
687 NS_ENSURE_STATE(GetOwner() && !mCanceled);
688 GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority());
689 return NS_OK;
692 NS_IMETHODIMP imgRequestProxy::AdjustPriority(int32_t priority)
694 // We don't require |!mCanceled| here. This may be called even if we're
695 // cancelled, because it's invoked as part of the process of removing an image
696 // from the load group.
697 NS_ENSURE_STATE(GetOwner());
698 GetOwner()->AdjustPriority(this, priority);
699 return NS_OK;
702 /** nsISecurityInfoProvider methods **/
704 NS_IMETHODIMP imgRequestProxy::GetSecurityInfo(nsISupports** _retval)
706 if (GetOwner())
707 return GetOwner()->GetSecurityInfo(_retval);
709 *_retval = nullptr;
710 return NS_OK;
713 NS_IMETHODIMP imgRequestProxy::GetHasTransferredData(bool* hasData)
715 if (GetOwner()) {
716 *hasData = GetOwner()->HasTransferredData();
717 } else {
718 // The safe thing to do is to claim we have data
719 *hasData = true;
721 return NS_OK;
724 void imgRequestProxy::OnStartDecode()
726 // This notification is deliberately not propagated since there are no
727 // listeners who care about it.
728 if (GetOwner()) {
729 // In the case of streaming jpegs, it is possible to get multiple
730 // OnStartDecodes which indicates the beginning of a new decode. The cache
731 // entry's size therefore needs to be reset to 0 here. If we do not do
732 // this, the code in ProgressTrackerObserver::OnStopFrame will continue to
733 // increase the data size cumulatively.
734 GetOwner()->ResetCacheEntry();
738 static const char*
739 NotificationTypeToString(int32_t aType)
741 switch(aType)
743 case imgINotificationObserver::SIZE_AVAILABLE: return "SIZE_AVAILABLE";
744 case imgINotificationObserver::FRAME_UPDATE: return "FRAME_UPDATE";
745 case imgINotificationObserver::FRAME_COMPLETE: return "FRAME_COMPLETE";
746 case imgINotificationObserver::LOAD_COMPLETE: return "LOAD_COMPLETE";
747 case imgINotificationObserver::DECODE_COMPLETE: return "DECODE_COMPLETE";
748 case imgINotificationObserver::DISCARD: return "DISCARD";
749 case imgINotificationObserver::UNLOCKED_DRAW: return "UNLOCKED_DRAW";
750 case imgINotificationObserver::IS_ANIMATED: return "IS_ANIMATED";
751 case imgINotificationObserver::HAS_TRANSPARENCY: return "HAS_TRANSPARENCY";
752 default:
753 NS_NOTREACHED("Notification list should be exhaustive");
754 return "(unknown notification)";
758 void
759 imgRequestProxy::Notify(int32_t aType, const nsIntRect* aRect)
761 MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE,
762 "Should call OnLoadComplete");
764 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::Notify", "type",
765 NotificationTypeToString(aType));
767 if (!mListener || mCanceled) {
768 return;
771 // Make sure the listener stays alive while we notify.
772 nsCOMPtr<imgINotificationObserver> listener(mListener);
774 mListener->Notify(this, aType, aRect);
777 void
778 imgRequestProxy::OnLoadComplete(bool aLastPart)
780 #ifdef PR_LOGGING
781 nsAutoCString name;
782 GetName(name);
783 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnLoadComplete", "name", name.get());
784 #endif
785 // There's all sorts of stuff here that could kill us (the OnStopRequest call
786 // on the listener, the removal from the loadgroup, the release of the
787 // listener, etc). Don't let them do it.
788 nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
790 if (mListener && !mCanceled) {
791 // Hold a ref to the listener while we call it, just in case.
792 nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
793 mListener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
796 // If we're expecting more data from a multipart channel, re-add ourself
797 // to the loadgroup so that the document doesn't lose track of the load.
798 // If the request is already a background request and there's more data
799 // coming, we can just leave the request in the loadgroup as-is.
800 if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
801 RemoveFromLoadGroup(aLastPart);
802 // More data is coming, so change the request to be a background request
803 // and put it back in the loadgroup.
804 if (!aLastPart) {
805 mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
806 AddToLoadGroup();
810 if (mListenerIsStrongRef && aLastPart) {
811 NS_PRECONDITION(mListener, "How did that happen?");
812 // Drop our strong ref to the listener now that we're done with
813 // everything. Note that this can cancel us and other fun things
814 // like that. Don't add anything in this method after this point.
815 imgINotificationObserver* obs = mListener;
816 mListenerIsStrongRef = false;
817 NS_RELEASE(obs);
821 void
822 imgRequestProxy::BlockOnload()
824 #ifdef PR_LOGGING
825 nsAutoCString name;
826 GetName(name);
827 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::BlockOnload", "name", name.get());
828 #endif
830 nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
831 if (blocker) {
832 blocker->BlockOnload(this);
836 void
837 imgRequestProxy::UnblockOnload()
839 #ifdef PR_LOGGING
840 nsAutoCString name;
841 GetName(name);
842 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::UnblockOnload", "name", name.get());
843 #endif
845 nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
846 if (blocker) {
847 blocker->UnblockOnload(this);
851 void imgRequestProxy::NullOutListener()
853 // If we have animation consumers, then they don't matter anymore
854 if (mListener)
855 ClearAnimationConsumers();
857 if (mListenerIsStrongRef) {
858 // Releasing could do weird reentery stuff, so just play it super-safe
859 nsCOMPtr<imgINotificationObserver> obs;
860 obs.swap(mListener);
861 mListenerIsStrongRef = false;
862 } else {
863 mListener = nullptr;
867 NS_IMETHODIMP
868 imgRequestProxy::GetStaticRequest(imgIRequest** aReturn)
870 imgRequestProxy *proxy;
871 nsresult result = GetStaticRequest(&proxy);
872 *aReturn = proxy;
873 return result;
876 nsresult
877 imgRequestProxy::GetStaticRequest(imgRequestProxy** aReturn)
879 *aReturn = nullptr;
880 nsRefPtr<Image> image = GetImage();
882 bool animated;
883 if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
884 // Early exit - we're not animated, so we don't have to do anything.
885 NS_ADDREF(*aReturn = this);
886 return NS_OK;
889 // Check for errors in the image. Callers code rely on GetStaticRequest
890 // failing in this case, though with FrozenImage there's no technical reason
891 // for it anymore.
892 if (image->HasError()) {
893 return NS_ERROR_FAILURE;
896 // We are animated. We need to create a frozen version of this image.
897 nsRefPtr<Image> frozenImage = ImageOps::Freeze(image);
899 // Create a static imgRequestProxy with our new extracted frame.
900 nsCOMPtr<nsIPrincipal> currentPrincipal;
901 GetImagePrincipal(getter_AddRefs(currentPrincipal));
902 nsRefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frozenImage,
903 currentPrincipal);
904 req->Init(nullptr, nullptr, mURI, nullptr);
906 NS_ADDREF(*aReturn = req);
908 return NS_OK;
911 void imgRequestProxy::NotifyListener()
913 // It would be nice to notify the observer directly in the status tracker
914 // instead of through the proxy, but there are several places we do extra
915 // processing when we receive notifications (like OnStopRequest()), and we
916 // need to check mCanceled everywhere too.
918 nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
919 if (GetOwner()) {
920 // Send the notifications to our listener asynchronously.
921 progressTracker->Notify(this);
922 } else {
923 // We don't have an imgRequest, so we can only notify the clone of our
924 // current state, but we still have to do that asynchronously.
925 NS_ABORT_IF_FALSE(HasImage(),
926 "if we have no imgRequest, we should have an Image");
927 progressTracker->NotifyCurrentState(this);
931 void imgRequestProxy::SyncNotifyListener()
933 // It would be nice to notify the observer directly in the status tracker
934 // instead of through the proxy, but there are several places we do extra
935 // processing when we receive notifications (like OnStopRequest()), and we
936 // need to check mCanceled everywhere too.
938 nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
939 progressTracker->SyncNotify(this);
942 void
943 imgRequestProxy::SetHasImage()
945 nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
946 MOZ_ASSERT(progressTracker);
947 nsRefPtr<Image> image = progressTracker->GetImage();
948 MOZ_ASSERT(image);
950 // Force any private status related to the owner to reflect
951 // the presence of an image;
952 mBehaviour->SetOwner(mBehaviour->GetOwner());
954 // Apply any locks we have
955 for (uint32_t i = 0; i < mLockCount; ++i)
956 image->LockImage();
958 // Apply any animation consumers we have
959 for (uint32_t i = 0; i < mAnimationConsumers; i++)
960 image->IncrementAnimationConsumers();
963 already_AddRefed<ProgressTracker>
964 imgRequestProxy::GetProgressTracker() const
966 return mBehaviour->GetProgressTracker();
969 already_AddRefed<mozilla::image::Image>
970 imgRequestProxy::GetImage() const
972 return mBehaviour->GetImage();
975 bool
976 RequestBehaviour::HasImage() const
978 if (!mOwnerHasImage)
979 return false;
980 nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
981 return progressTracker ? progressTracker->HasImage() : false;
984 bool
985 imgRequestProxy::HasImage() const
987 return mBehaviour->HasImage();
990 imgRequest*
991 imgRequestProxy::GetOwner() const
993 return mBehaviour->GetOwner();
996 ////////////////// imgRequestProxyStatic methods
998 class StaticBehaviour : public ProxyBehaviour
1000 public:
1001 explicit StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
1003 virtual already_AddRefed<mozilla::image::Image>
1004 GetImage() const MOZ_OVERRIDE {
1005 nsRefPtr<mozilla::image::Image> image = mImage;
1006 return image.forget();
1009 virtual bool HasImage() const MOZ_OVERRIDE {
1010 return mImage;
1013 virtual already_AddRefed<ProgressTracker> GetProgressTracker() const MOZ_OVERRIDE {
1014 return mImage->GetProgressTracker();
1017 virtual imgRequest* GetOwner() const MOZ_OVERRIDE {
1018 return nullptr;
1021 virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE {
1022 MOZ_ASSERT(!aOwner, "We shouldn't be giving static requests a non-null owner.");
1025 private:
1026 // Our image. We have to hold a strong reference here, because that's normally
1027 // the job of the underlying request.
1028 nsRefPtr<mozilla::image::Image> mImage;
1031 imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage,
1032 nsIPrincipal* aPrincipal)
1033 : mPrincipal(aPrincipal)
1035 mBehaviour = new StaticBehaviour(aImage);
1038 NS_IMETHODIMP imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal **aPrincipal)
1040 if (!mPrincipal)
1041 return NS_ERROR_FAILURE;
1043 NS_ADDREF(*aPrincipal = mPrincipal);
1045 return NS_OK;
1048 nsresult
1049 imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver,
1050 imgRequestProxy** aClone)
1052 return PerformClone(aObserver, NewStaticProxy, aClone);