Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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 "imgRequestProxy.h"
8 #include "imgIOnloadBlocker.h"
10 #include "Image.h"
11 #include "ImageOps.h"
12 #include "nsError.h"
13 #include "ImageLogging.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
25 {
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<imgStatusTracker> GetStatusTracker() const = 0;
32 virtual imgRequest* GetOwner() const = 0;
33 virtual void SetOwner(imgRequest* aOwner) = 0;
34 };
36 class RequestBehaviour : public ProxyBehaviour
37 {
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<imgStatusTracker> GetStatusTracker() const MOZ_OVERRIDE;
45 virtual imgRequest* GetOwner() const MOZ_OVERRIDE {
46 return mOwner;
47 }
49 virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE {
50 mOwner = aOwner;
52 if (mOwner) {
53 nsRefPtr<imgStatusTracker> ownerStatusTracker = GetStatusTracker();
54 mOwnerHasImage = ownerStatusTracker && ownerStatusTracker->HasImage();
55 } else {
56 mOwnerHasImage = false;
57 }
58 }
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;
70 };
72 already_AddRefed<mozilla::image::Image>
73 RequestBehaviour::GetImage() const
74 {
75 if (!mOwnerHasImage)
76 return nullptr;
77 nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
78 return statusTracker->GetImage();
79 }
81 already_AddRefed<imgStatusTracker>
82 RequestBehaviour::GetStatusTracker() const
83 {
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->GetStatusTracker() instead of just
88 // mOwner->mStatusTracker -- we might have a null mImage and yet have an
89 // mOwner with a non-null mImage (and a null mStatusTracker pointer).
90 return mOwner->GetStatusTracker();
91 }
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),
117 mSentStartContainer(false)
118 {
119 /* member initializers and constructor code */
121 }
123 imgRequestProxy::~imgRequestProxy()
124 {
125 /* destructor code */
126 NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!");
128 // Unlock the image the proper number of times if we're holding locks on it.
129 // Note that UnlockImage() decrements mLockCount each time it's called.
130 while (mLockCount)
131 UnlockImage();
133 ClearAnimationConsumers();
135 // Explicitly set mListener to null to ensure that the RemoveProxy
136 // call below can't send |this| to an arbitrary listener while |this|
137 // is being destroyed. This is all belt-and-suspenders in view of the
138 // above assert.
139 NullOutListener();
141 if (GetOwner()) {
142 /* Call RemoveProxy with a successful status. This will keep the
143 channel, if still downloading data, from being canceled if 'this' is
144 the last observer. This allows the image to continue to download and
145 be cached even if no one is using it currently.
146 */
147 mCanceled = true;
148 GetOwner()->RemoveProxy(this, NS_OK);
149 }
150 }
152 nsresult imgRequestProxy::Init(imgRequest* aOwner,
153 nsILoadGroup* aLoadGroup,
154 ImageURL* aURI,
155 imgINotificationObserver* aObserver)
156 {
157 NS_PRECONDITION(!GetOwner() && !mListener, "imgRequestProxy is already initialized");
159 LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request", aOwner);
161 NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init");
163 mBehaviour->SetOwner(aOwner);
164 mListener = aObserver;
165 // Make sure to addref mListener before the AddProxy call below, since
166 // that call might well want to release it if the imgRequest has
167 // already seen OnStopRequest.
168 if (mListener) {
169 mListenerIsStrongRef = true;
170 NS_ADDREF(mListener);
171 }
172 mLoadGroup = aLoadGroup;
173 mURI = aURI;
175 // Note: AddProxy won't send all the On* notifications immediately
176 if (GetOwner())
177 GetOwner()->AddProxy(this);
179 return NS_OK;
180 }
182 nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
183 {
184 NS_PRECONDITION(GetOwner(), "Cannot ChangeOwner on a proxy without an owner!");
186 if (mCanceled) {
187 // Ensure that this proxy has received all notifications to date before
188 // we clean it up when removing it from the old owner below.
189 SyncNotifyListener();
190 }
192 // If we're holding locks, unlock the old image.
193 // Note that UnlockImage decrements mLockCount each time it's called.
194 uint32_t oldLockCount = mLockCount;
195 while (mLockCount)
196 UnlockImage();
198 // If we're holding animation requests, undo them.
199 uint32_t oldAnimationConsumers = mAnimationConsumers;
200 ClearAnimationConsumers();
202 // Were we decoded before?
203 bool wasDecoded = false;
204 nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
205 if (statusTracker->HasImage() &&
206 statusTracker->GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE) {
207 wasDecoded = true;
208 }
210 GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
212 mBehaviour->SetOwner(aNewOwner);
214 // If we were locked, apply the locks here
215 for (uint32_t i = 0; i < oldLockCount; i++)
216 LockImage();
218 // If we had animation requests, restore them here. Note that we
219 // do this *after* RemoveProxy, which clears out animation consumers
220 // (see bug 601723).
221 for (uint32_t i = 0; i < oldAnimationConsumers; i++)
222 IncrementAnimationConsumers();
224 GetOwner()->AddProxy(this);
226 // If we were decoded, or if we'd previously requested a decode, request a
227 // decode on the new image
228 if (wasDecoded || mDecodeRequested)
229 GetOwner()->StartDecoding();
231 return NS_OK;
232 }
234 void imgRequestProxy::AddToLoadGroup()
235 {
236 NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
238 if (!mIsInLoadGroup && mLoadGroup) {
239 mLoadGroup->AddRequest(this, nullptr);
240 mIsInLoadGroup = true;
241 }
242 }
244 void imgRequestProxy::RemoveFromLoadGroup(bool releaseLoadGroup)
245 {
246 if (!mIsInLoadGroup)
247 return;
249 /* calling RemoveFromLoadGroup may cause the document to finish
250 loading, which could result in our death. We need to make sure
251 that we stay alive long enough to fight another battle... at
252 least until we exit this function.
253 */
254 nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
256 mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
257 mIsInLoadGroup = false;
259 if (releaseLoadGroup) {
260 // We're done with the loadgroup, release it.
261 mLoadGroup = nullptr;
262 }
263 }
266 /** nsIRequest / imgIRequest methods **/
268 /* readonly attribute wstring name; */
269 NS_IMETHODIMP imgRequestProxy::GetName(nsACString &aName)
270 {
271 aName.Truncate();
273 if (mURI)
274 mURI->GetSpec(aName);
276 return NS_OK;
277 }
279 /* boolean isPending (); */
280 NS_IMETHODIMP imgRequestProxy::IsPending(bool *_retval)
281 {
282 return NS_ERROR_NOT_IMPLEMENTED;
283 }
285 /* readonly attribute nsresult status; */
286 NS_IMETHODIMP imgRequestProxy::GetStatus(nsresult *aStatus)
287 {
288 return NS_ERROR_NOT_IMPLEMENTED;
289 }
291 /* void cancel (in nsresult status); */
292 NS_IMETHODIMP imgRequestProxy::Cancel(nsresult status)
293 {
294 if (mCanceled)
295 return NS_ERROR_FAILURE;
297 LOG_SCOPE(GetImgLog(), "imgRequestProxy::Cancel");
299 mCanceled = true;
301 nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
302 return NS_DispatchToCurrentThread(ev);
303 }
305 void
306 imgRequestProxy::DoCancel(nsresult status)
307 {
308 if (GetOwner()) {
309 GetOwner()->RemoveProxy(this, status);
310 }
312 NullOutListener();
313 }
315 /* void cancelAndForgetObserver (in nsresult aStatus); */
316 NS_IMETHODIMP imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
317 {
318 // If mCanceled is true but mListener is non-null, that means
319 // someone called Cancel() on us but the imgCancelRunnable is still
320 // pending. We still need to null out mListener before returning
321 // from this function in this case. That means we want to do the
322 // RemoveProxy call right now, because we need to deliver the
323 // onStopRequest.
324 if (mCanceled && !mListener)
325 return NS_ERROR_FAILURE;
327 LOG_SCOPE(GetImgLog(), "imgRequestProxy::CancelAndForgetObserver");
329 mCanceled = true;
331 // Now cheat and make sure our removal from loadgroup happens async
332 bool oldIsInLoadGroup = mIsInLoadGroup;
333 mIsInLoadGroup = false;
335 if (GetOwner()) {
336 GetOwner()->RemoveProxy(this, aStatus);
337 }
339 mIsInLoadGroup = oldIsInLoadGroup;
341 if (mIsInLoadGroup) {
342 nsCOMPtr<nsIRunnable> ev =
343 NS_NewRunnableMethod(this, &imgRequestProxy::DoRemoveFromLoadGroup);
344 NS_DispatchToCurrentThread(ev);
345 }
347 NullOutListener();
349 return NS_OK;
350 }
352 /* void startDecode (); */
353 NS_IMETHODIMP
354 imgRequestProxy::StartDecoding()
355 {
356 if (!GetOwner())
357 return NS_ERROR_FAILURE;
359 // Flag this, so we know to transfer the request if our owner changes
360 mDecodeRequested = true;
362 // Forward the request
363 return GetOwner()->StartDecoding();
364 }
366 /* void requestDecode (); */
367 NS_IMETHODIMP
368 imgRequestProxy::RequestDecode()
369 {
370 if (!GetOwner())
371 return NS_ERROR_FAILURE;
373 // Flag this, so we know to transfer the request if our owner changes
374 mDecodeRequested = true;
376 // Forward the request
377 return GetOwner()->RequestDecode();
378 }
381 /* void lockImage (); */
382 NS_IMETHODIMP
383 imgRequestProxy::LockImage()
384 {
385 mLockCount++;
386 nsRefPtr<Image> image = GetImage();
387 if (image)
388 return image->LockImage();
389 return NS_OK;
390 }
392 /* void unlockImage (); */
393 NS_IMETHODIMP
394 imgRequestProxy::UnlockImage()
395 {
396 NS_ABORT_IF_FALSE(mLockCount > 0, "calling unlock but no locks!");
398 mLockCount--;
399 nsRefPtr<Image> image = GetImage();
400 if (image)
401 return image->UnlockImage();
402 return NS_OK;
403 }
405 /* void requestDiscard (); */
406 NS_IMETHODIMP
407 imgRequestProxy::RequestDiscard()
408 {
409 nsRefPtr<Image> image = GetImage();
410 if (image)
411 return image->RequestDiscard();
412 return NS_OK;
413 }
415 NS_IMETHODIMP
416 imgRequestProxy::IncrementAnimationConsumers()
417 {
418 mAnimationConsumers++;
419 nsRefPtr<Image> image = GetImage();
420 if (image)
421 image->IncrementAnimationConsumers();
422 return NS_OK;
423 }
425 NS_IMETHODIMP
426 imgRequestProxy::DecrementAnimationConsumers()
427 {
428 // We may get here if some responsible code called Increment,
429 // then called us, but we have meanwhile called ClearAnimationConsumers
430 // because we needed to get rid of them earlier (see
431 // imgRequest::RemoveProxy), and hence have nothing left to
432 // decrement. (In such a case we got rid of the animation consumers
433 // early, but not the observer.)
434 if (mAnimationConsumers > 0) {
435 mAnimationConsumers--;
436 nsRefPtr<Image> image = GetImage();
437 if (image)
438 image->DecrementAnimationConsumers();
439 }
440 return NS_OK;
441 }
443 void
444 imgRequestProxy::ClearAnimationConsumers()
445 {
446 while (mAnimationConsumers > 0)
447 DecrementAnimationConsumers();
448 }
450 /* void suspend (); */
451 NS_IMETHODIMP imgRequestProxy::Suspend()
452 {
453 return NS_ERROR_NOT_IMPLEMENTED;
454 }
456 /* void resume (); */
457 NS_IMETHODIMP imgRequestProxy::Resume()
458 {
459 return NS_ERROR_NOT_IMPLEMENTED;
460 }
462 /* attribute nsILoadGroup loadGroup */
463 NS_IMETHODIMP imgRequestProxy::GetLoadGroup(nsILoadGroup **loadGroup)
464 {
465 NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
466 return NS_OK;
467 }
468 NS_IMETHODIMP imgRequestProxy::SetLoadGroup(nsILoadGroup *loadGroup)
469 {
470 mLoadGroup = loadGroup;
471 return NS_OK;
472 }
474 /* attribute nsLoadFlags loadFlags */
475 NS_IMETHODIMP imgRequestProxy::GetLoadFlags(nsLoadFlags *flags)
476 {
477 *flags = mLoadFlags;
478 return NS_OK;
479 }
480 NS_IMETHODIMP imgRequestProxy::SetLoadFlags(nsLoadFlags flags)
481 {
482 mLoadFlags = flags;
483 return NS_OK;
484 }
486 /** imgIRequest methods **/
488 /* attribute imgIContainer image; */
489 NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer **aImage)
490 {
491 NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
492 // It's possible that our owner has an image but hasn't notified us of it -
493 // that'll happen if we get Canceled before the owner instantiates its image
494 // (because Canceling unregisters us as a listener on mOwner). If we're
495 // in that situation, just grab the image off of mOwner.
496 nsRefPtr<Image> image = GetImage();
497 nsCOMPtr<imgIContainer> imageToReturn;
498 if (image)
499 imageToReturn = do_QueryInterface(image);
500 if (!imageToReturn && GetOwner())
501 imageToReturn = GetOwner()->mImage;
503 if (!imageToReturn)
504 return NS_ERROR_FAILURE;
506 imageToReturn.swap(*aImage);
508 return NS_OK;
509 }
511 /* readonly attribute unsigned long imageStatus; */
512 NS_IMETHODIMP imgRequestProxy::GetImageStatus(uint32_t *aStatus)
513 {
514 nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
515 *aStatus = statusTracker->GetImageStatus();
517 return NS_OK;
518 }
520 /* readonly attribute nsIURI URI; */
521 NS_IMETHODIMP imgRequestProxy::GetURI(nsIURI **aURI)
522 {
523 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
524 nsCOMPtr<nsIURI> uri = mURI->ToIURI();
525 uri.forget(aURI);
526 return NS_OK;
527 }
529 nsresult imgRequestProxy::GetURI(ImageURL **aURI)
530 {
531 if (!mURI)
532 return NS_ERROR_FAILURE;
534 NS_ADDREF(*aURI = mURI);
536 return NS_OK;
537 }
539 /* readonly attribute imgINotificationObserver notificationObserver; */
540 NS_IMETHODIMP imgRequestProxy::GetNotificationObserver(imgINotificationObserver **aObserver)
541 {
542 *aObserver = mListener;
543 NS_IF_ADDREF(*aObserver);
544 return NS_OK;
545 }
547 /* readonly attribute string mimeType; */
548 NS_IMETHODIMP imgRequestProxy::GetMimeType(char **aMimeType)
549 {
550 if (!GetOwner())
551 return NS_ERROR_FAILURE;
553 const char *type = GetOwner()->GetMimeType();
554 if (!type)
555 return NS_ERROR_FAILURE;
557 *aMimeType = NS_strdup(type);
559 return NS_OK;
560 }
562 static imgRequestProxy* NewProxy(imgRequestProxy* /*aThis*/)
563 {
564 return new imgRequestProxy();
565 }
567 imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis)
568 {
569 nsCOMPtr<nsIPrincipal> currentPrincipal;
570 aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal));
571 nsRefPtr<Image> image = aThis->GetImage();
572 return new imgRequestProxyStatic(image, currentPrincipal);
573 }
575 NS_IMETHODIMP imgRequestProxy::Clone(imgINotificationObserver* aObserver,
576 imgIRequest** aClone)
577 {
578 nsresult result;
579 imgRequestProxy* proxy;
580 result = Clone(aObserver, &proxy);
581 *aClone = proxy;
582 return result;
583 }
585 nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
586 imgRequestProxy** aClone)
587 {
588 return PerformClone(aObserver, NewProxy, aClone);
589 }
591 nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
592 imgRequestProxy* (aAllocFn)(imgRequestProxy*),
593 imgRequestProxy** aClone)
594 {
595 NS_PRECONDITION(aClone, "Null out param");
597 LOG_SCOPE(GetImgLog(), "imgRequestProxy::Clone");
599 *aClone = nullptr;
600 nsRefPtr<imgRequestProxy> clone = aAllocFn(this);
602 // It is important to call |SetLoadFlags()| before calling |Init()| because
603 // |Init()| adds the request to the loadgroup.
604 // When a request is added to a loadgroup, its load flags are merged
605 // with the load flags of the loadgroup.
606 // XXXldb That's not true anymore. Stuff from imgLoader adds the
607 // request to the loadgroup.
608 clone->SetLoadFlags(mLoadFlags);
609 nsresult rv = clone->Init(mBehaviour->GetOwner(), mLoadGroup, mURI, aObserver);
610 if (NS_FAILED(rv))
611 return rv;
613 // Assign to *aClone before calling Notify so that if the caller expects to
614 // only be notified for requests it's already holding pointers to it won't be
615 // surprised.
616 NS_ADDREF(*aClone = clone);
618 // This is wrong!!! We need to notify asynchronously, but there's code that
619 // assumes that we don't. This will be fixed in bug 580466.
620 clone->SyncNotifyListener();
622 return NS_OK;
623 }
625 /* readonly attribute nsIPrincipal imagePrincipal; */
626 NS_IMETHODIMP imgRequestProxy::GetImagePrincipal(nsIPrincipal **aPrincipal)
627 {
628 if (!GetOwner())
629 return NS_ERROR_FAILURE;
631 NS_ADDREF(*aPrincipal = GetOwner()->GetPrincipal());
632 return NS_OK;
633 }
635 /* readonly attribute bool multipart; */
636 NS_IMETHODIMP imgRequestProxy::GetMultipart(bool *aMultipart)
637 {
638 if (!GetOwner())
639 return NS_ERROR_FAILURE;
641 *aMultipart = GetOwner()->GetMultipart();
643 return NS_OK;
644 }
646 /* readonly attribute int32_t CORSMode; */
647 NS_IMETHODIMP imgRequestProxy::GetCORSMode(int32_t* aCorsMode)
648 {
649 if (!GetOwner())
650 return NS_ERROR_FAILURE;
652 *aCorsMode = GetOwner()->GetCORSMode();
654 return NS_OK;
655 }
657 /** nsISupportsPriority methods **/
659 NS_IMETHODIMP imgRequestProxy::GetPriority(int32_t *priority)
660 {
661 NS_ENSURE_STATE(GetOwner());
662 *priority = GetOwner()->Priority();
663 return NS_OK;
664 }
666 NS_IMETHODIMP imgRequestProxy::SetPriority(int32_t priority)
667 {
668 NS_ENSURE_STATE(GetOwner() && !mCanceled);
669 GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority());
670 return NS_OK;
671 }
673 NS_IMETHODIMP imgRequestProxy::AdjustPriority(int32_t priority)
674 {
675 // We don't require |!mCanceled| here. This may be called even if we're
676 // cancelled, because it's invoked as part of the process of removing an image
677 // from the load group.
678 NS_ENSURE_STATE(GetOwner());
679 GetOwner()->AdjustPriority(this, priority);
680 return NS_OK;
681 }
683 /** nsISecurityInfoProvider methods **/
685 NS_IMETHODIMP imgRequestProxy::GetSecurityInfo(nsISupports** _retval)
686 {
687 if (GetOwner())
688 return GetOwner()->GetSecurityInfo(_retval);
690 *_retval = nullptr;
691 return NS_OK;
692 }
694 NS_IMETHODIMP imgRequestProxy::GetHasTransferredData(bool* hasData)
695 {
696 if (GetOwner()) {
697 *hasData = GetOwner()->HasTransferredData();
698 } else {
699 // The safe thing to do is to claim we have data
700 *hasData = true;
701 }
702 return NS_OK;
703 }
705 /** imgDecoderObserver methods **/
707 void imgRequestProxy::OnStartDecode()
708 {
709 // This notification is deliberately not propagated since there are no
710 // listeners who care about it.
711 if (GetOwner()) {
712 // In the case of streaming jpegs, it is possible to get multiple
713 // OnStartDecodes which indicates the beginning of a new decode. The cache
714 // entry's size therefore needs to be reset to 0 here. If we do not do
715 // this, the code in imgStatusTrackerObserver::OnStopFrame will continue to
716 // increase the data size cumulatively.
717 GetOwner()->ResetCacheEntry();
718 }
719 }
721 void imgRequestProxy::OnStartContainer()
722 {
723 LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStartContainer");
725 if (mListener && !mCanceled && !mSentStartContainer) {
726 // Hold a ref to the listener while we call it, just in case.
727 nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
728 mListener->Notify(this, imgINotificationObserver::SIZE_AVAILABLE, nullptr);
729 mSentStartContainer = true;
730 }
731 }
733 void imgRequestProxy::OnFrameUpdate(const nsIntRect * rect)
734 {
735 LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDataAvailable");
737 if (mListener && !mCanceled) {
738 // Hold a ref to the listener while we call it, just in case.
739 nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
740 mListener->Notify(this, imgINotificationObserver::FRAME_UPDATE, rect);
741 }
742 }
744 void imgRequestProxy::OnStopFrame()
745 {
746 LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStopFrame");
748 if (mListener && !mCanceled) {
749 // Hold a ref to the listener while we call it, just in case.
750 nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
751 mListener->Notify(this, imgINotificationObserver::FRAME_COMPLETE, nullptr);
752 }
753 }
755 void imgRequestProxy::OnStopDecode()
756 {
757 LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStopDecode");
759 if (mListener && !mCanceled) {
760 // Hold a ref to the listener while we call it, just in case.
761 nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
762 mListener->Notify(this, imgINotificationObserver::DECODE_COMPLETE, nullptr);
763 }
765 if (GetOwner()) {
766 // We finished the decode, and thus have the decoded frames. Update the cache
767 // entry size to take this into account.
768 GetOwner()->UpdateCacheEntrySize();
770 // Multipart needs reset for next OnStartContainer.
771 if (GetOwner()->GetMultipart())
772 mSentStartContainer = false;
773 }
774 }
776 void imgRequestProxy::OnDiscard()
777 {
778 LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDiscard");
780 if (mListener && !mCanceled) {
781 // Hold a ref to the listener while we call it, just in case.
782 nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
783 mListener->Notify(this, imgINotificationObserver::DISCARD, nullptr);
784 }
785 if (GetOwner()) {
786 // Update the cache entry size, since we just got rid of frame data.
787 GetOwner()->UpdateCacheEntrySize();
788 }
789 }
791 void imgRequestProxy::OnUnlockedDraw()
792 {
793 LOG_FUNC(GetImgLog(), "imgRequestProxy::OnUnlockedDraw");
795 if (mListener && !mCanceled) {
796 // Hold a ref to the listener while we call it, just in case.
797 nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
798 mListener->Notify(this, imgINotificationObserver::UNLOCKED_DRAW, nullptr);
799 }
800 }
802 void imgRequestProxy::OnImageIsAnimated()
803 {
804 LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageIsAnimated");
805 if (mListener && !mCanceled) {
806 // Hold a ref to the listener while we call it, just in case.
807 nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
808 mListener->Notify(this, imgINotificationObserver::IS_ANIMATED, nullptr);
809 }
810 }
812 void imgRequestProxy::OnStartRequest()
813 {
814 #ifdef PR_LOGGING
815 nsAutoCString name;
816 GetName(name);
817 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnStartRequest", "name", name.get());
818 #endif
819 }
821 void imgRequestProxy::OnStopRequest(bool lastPart)
822 {
823 #ifdef PR_LOGGING
824 nsAutoCString name;
825 GetName(name);
826 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnStopRequest", "name", name.get());
827 #endif
828 // There's all sorts of stuff here that could kill us (the OnStopRequest call
829 // on the listener, the removal from the loadgroup, the release of the
830 // listener, etc). Don't let them do it.
831 nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
833 if (mListener && !mCanceled) {
834 // Hold a ref to the listener while we call it, just in case.
835 nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
836 mListener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
837 }
839 // If we're expecting more data from a multipart channel, re-add ourself
840 // to the loadgroup so that the document doesn't lose track of the load.
841 // If the request is already a background request and there's more data
842 // coming, we can just leave the request in the loadgroup as-is.
843 if (lastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
844 RemoveFromLoadGroup(lastPart);
845 // More data is coming, so change the request to be a background request
846 // and put it back in the loadgroup.
847 if (!lastPart) {
848 mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
849 AddToLoadGroup();
850 }
851 }
853 if (mListenerIsStrongRef && lastPart) {
854 NS_PRECONDITION(mListener, "How did that happen?");
855 // Drop our strong ref to the listener now that we're done with
856 // everything. Note that this can cancel us and other fun things
857 // like that. Don't add anything in this method after this point.
858 imgINotificationObserver* obs = mListener;
859 mListenerIsStrongRef = false;
860 NS_RELEASE(obs);
861 }
862 }
864 void imgRequestProxy::BlockOnload()
865 {
866 #ifdef PR_LOGGING
867 nsAutoCString name;
868 GetName(name);
869 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::BlockOnload", "name", name.get());
870 #endif
872 nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
873 if (blocker) {
874 blocker->BlockOnload(this);
875 }
876 }
878 void imgRequestProxy::UnblockOnload()
879 {
880 #ifdef PR_LOGGING
881 nsAutoCString name;
882 GetName(name);
883 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::UnblockOnload", "name", name.get());
884 #endif
886 nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
887 if (blocker) {
888 blocker->UnblockOnload(this);
889 }
890 }
892 void imgRequestProxy::NullOutListener()
893 {
894 // If we have animation consumers, then they don't matter anymore
895 if (mListener)
896 ClearAnimationConsumers();
898 if (mListenerIsStrongRef) {
899 // Releasing could do weird reentery stuff, so just play it super-safe
900 nsCOMPtr<imgINotificationObserver> obs;
901 obs.swap(mListener);
902 mListenerIsStrongRef = false;
903 } else {
904 mListener = nullptr;
905 }
906 }
908 NS_IMETHODIMP
909 imgRequestProxy::GetStaticRequest(imgIRequest** aReturn)
910 {
911 imgRequestProxy *proxy;
912 nsresult result = GetStaticRequest(&proxy);
913 *aReturn = proxy;
914 return result;
915 }
917 nsresult
918 imgRequestProxy::GetStaticRequest(imgRequestProxy** aReturn)
919 {
920 *aReturn = nullptr;
921 nsRefPtr<Image> image = GetImage();
923 bool animated;
924 if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
925 // Early exit - we're not animated, so we don't have to do anything.
926 NS_ADDREF(*aReturn = this);
927 return NS_OK;
928 }
930 // Check for errors in the image. Callers code rely on GetStaticRequest
931 // failing in this case, though with FrozenImage there's no technical reason
932 // for it anymore.
933 if (image->HasError()) {
934 return NS_ERROR_FAILURE;
935 }
937 // We are animated. We need to create a frozen version of this image.
938 nsRefPtr<Image> frozenImage = ImageOps::Freeze(image);
940 // Create a static imgRequestProxy with our new extracted frame.
941 nsCOMPtr<nsIPrincipal> currentPrincipal;
942 GetImagePrincipal(getter_AddRefs(currentPrincipal));
943 nsRefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frozenImage,
944 currentPrincipal);
945 req->Init(nullptr, nullptr, mURI, nullptr);
947 NS_ADDREF(*aReturn = req);
949 return NS_OK;
950 }
952 void imgRequestProxy::NotifyListener()
953 {
954 // It would be nice to notify the observer directly in the status tracker
955 // instead of through the proxy, but there are several places we do extra
956 // processing when we receive notifications (like OnStopRequest()), and we
957 // need to check mCanceled everywhere too.
959 nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
960 if (GetOwner()) {
961 // Send the notifications to our listener asynchronously.
962 statusTracker->Notify(this);
963 } else {
964 // We don't have an imgRequest, so we can only notify the clone of our
965 // current state, but we still have to do that asynchronously.
966 NS_ABORT_IF_FALSE(HasImage(),
967 "if we have no imgRequest, we should have an Image");
968 statusTracker->NotifyCurrentState(this);
969 }
970 }
972 void imgRequestProxy::SyncNotifyListener()
973 {
974 // It would be nice to notify the observer directly in the status tracker
975 // instead of through the proxy, but there are several places we do extra
976 // processing when we receive notifications (like OnStopRequest()), and we
977 // need to check mCanceled everywhere too.
979 nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
980 statusTracker->SyncNotify(this);
981 }
983 void
984 imgRequestProxy::SetHasImage()
985 {
986 nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
987 MOZ_ASSERT(statusTracker);
988 nsRefPtr<Image> image = statusTracker->GetImage();
989 MOZ_ASSERT(image);
991 // Force any private status related to the owner to reflect
992 // the presence of an image;
993 mBehaviour->SetOwner(mBehaviour->GetOwner());
995 // Apply any locks we have
996 for (uint32_t i = 0; i < mLockCount; ++i)
997 image->LockImage();
999 // Apply any animation consumers we have
1000 for (uint32_t i = 0; i < mAnimationConsumers; i++)
1001 image->IncrementAnimationConsumers();
1002 }
1004 already_AddRefed<imgStatusTracker>
1005 imgRequestProxy::GetStatusTracker() const
1006 {
1007 return mBehaviour->GetStatusTracker();
1008 }
1010 already_AddRefed<mozilla::image::Image>
1011 imgRequestProxy::GetImage() const
1012 {
1013 return mBehaviour->GetImage();
1014 }
1016 bool
1017 RequestBehaviour::HasImage() const
1018 {
1019 if (!mOwnerHasImage)
1020 return false;
1021 nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
1022 return statusTracker ? statusTracker->HasImage() : false;
1023 }
1025 bool
1026 imgRequestProxy::HasImage() const
1027 {
1028 return mBehaviour->HasImage();
1029 }
1031 imgRequest*
1032 imgRequestProxy::GetOwner() const
1033 {
1034 return mBehaviour->GetOwner();
1035 }
1037 ////////////////// imgRequestProxyStatic methods
1039 class StaticBehaviour : public ProxyBehaviour
1040 {
1041 public:
1042 StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
1044 virtual already_AddRefed<mozilla::image::Image>
1045 GetImage() const MOZ_OVERRIDE {
1046 nsRefPtr<mozilla::image::Image> image = mImage;
1047 return image.forget();
1048 }
1050 virtual bool HasImage() const MOZ_OVERRIDE {
1051 return mImage;
1052 }
1054 virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const MOZ_OVERRIDE {
1055 return mImage->GetStatusTracker();
1056 }
1058 virtual imgRequest* GetOwner() const MOZ_OVERRIDE {
1059 return nullptr;
1060 }
1062 virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE {
1063 MOZ_ASSERT(!aOwner, "We shouldn't be giving static requests a non-null owner.");
1064 }
1066 private:
1067 // Our image. We have to hold a strong reference here, because that's normally
1068 // the job of the underlying request.
1069 nsRefPtr<mozilla::image::Image> mImage;
1070 };
1072 imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage,
1073 nsIPrincipal* aPrincipal)
1074 : mPrincipal(aPrincipal)
1075 {
1076 mBehaviour = new StaticBehaviour(aImage);
1077 }
1079 NS_IMETHODIMP imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal **aPrincipal)
1080 {
1081 if (!mPrincipal)
1082 return NS_ERROR_FAILURE;
1084 NS_ADDREF(*aPrincipal = mPrincipal);
1086 return NS_OK;
1087 }
1089 nsresult
1090 imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver,
1091 imgRequestProxy** aClone)
1092 {
1093 return PerformClone(aObserver, NewStaticProxy, aClone);
1094 }