12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "ImageLogging.h"
- #include "imgRequestProxy.h"
- #include "imgIOnloadBlocker.h"
- #include "imgLoader.h"
- #include "Image.h"
- #include "ImageOps.h"
- #include "nsError.h"
- #include "nsCRTGlue.h"
- #include "imgINotificationObserver.h"
- using namespace mozilla::image;
- // The split of imgRequestProxy and imgRequestProxyStatic means that
- // certain overridden functions need to be usable in the destructor.
- // Since virtual functions can't be used in that way, this class
- // provides a behavioural trait for each class to use instead.
- class ProxyBehaviour
- {
- public:
- virtual ~ProxyBehaviour() {}
- virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0;
- virtual bool HasImage() const = 0;
- virtual already_AddRefed<ProgressTracker> GetProgressTracker() const = 0;
- virtual imgRequest* GetOwner() const = 0;
- virtual void SetOwner(imgRequest* aOwner) = 0;
- };
- class RequestBehaviour : public ProxyBehaviour
- {
- public:
- RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {}
- virtual already_AddRefed<mozilla::image::Image>GetImage() const override;
- virtual bool HasImage() const override;
- virtual already_AddRefed<ProgressTracker> GetProgressTracker() const override;
- virtual imgRequest* GetOwner() const override {
- return mOwner;
- }
- virtual void SetOwner(imgRequest* aOwner) override {
- mOwner = aOwner;
- if (mOwner) {
- RefPtr<ProgressTracker> ownerProgressTracker = GetProgressTracker();
- mOwnerHasImage = ownerProgressTracker && ownerProgressTracker->HasImage();
- } else {
- mOwnerHasImage = false;
- }
- }
- private:
- // We maintain the following invariant:
- // The proxy is registered at most with a single imgRequest as an observer,
- // and whenever it is, mOwner points to that object. This helps ensure that
- // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
- // from whatever request it was registered with (if any). This, in turn,
- // means that imgRequest::mObservers will not have any stale pointers in it.
- RefPtr<imgRequest> mOwner;
- bool mOwnerHasImage;
- };
- already_AddRefed<mozilla::image::Image>
- RequestBehaviour::GetImage() const
- {
- if (!mOwnerHasImage) {
- return nullptr;
- }
- RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
- return progressTracker->GetImage();
- }
- already_AddRefed<ProgressTracker>
- RequestBehaviour::GetProgressTracker() const
- {
- // NOTE: It's possible that our mOwner has an Image that it didn't notify
- // us about, if we were Canceled before its Image was constructed.
- // (Canceling removes us as an observer, so mOwner has no way to notify us).
- // That's why this method uses mOwner->GetProgressTracker() instead of just
- // mOwner->mProgressTracker -- we might have a null mImage and yet have an
- // mOwner with a non-null mImage (and a null mProgressTracker pointer).
- return mOwner->GetProgressTracker();
- }
- NS_IMPL_ADDREF(imgRequestProxy)
- NS_IMPL_RELEASE(imgRequestProxy)
- NS_INTERFACE_MAP_BEGIN(imgRequestProxy)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgIRequest)
- NS_INTERFACE_MAP_ENTRY(imgIRequest)
- NS_INTERFACE_MAP_ENTRY(nsIRequest)
- NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
- NS_INTERFACE_MAP_ENTRY(nsISecurityInfoProvider)
- NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel,
- TimedChannel() != nullptr)
- NS_INTERFACE_MAP_END
- imgRequestProxy::imgRequestProxy() :
- mBehaviour(new RequestBehaviour),
- mURI(nullptr),
- mListener(nullptr),
- mLoadFlags(nsIRequest::LOAD_NORMAL),
- mLockCount(0),
- mAnimationConsumers(0),
- mCanceled(false),
- mIsInLoadGroup(false),
- mListenerIsStrongRef(false),
- mDecodeRequested(false),
- mDeferNotifications(false)
- {
- /* member initializers and constructor code */
- }
- imgRequestProxy::~imgRequestProxy()
- {
- /* destructor code */
- NS_PRECONDITION(!mListener,
- "Someone forgot to properly cancel this request!");
- // Unlock the image the proper number of times if we're holding locks on
- // it. Note that UnlockImage() decrements mLockCount each time it's called.
- while (mLockCount) {
- UnlockImage();
- }
- ClearAnimationConsumers();
- // Explicitly set mListener to null to ensure that the RemoveProxy
- // call below can't send |this| to an arbitrary listener while |this|
- // is being destroyed. This is all belt-and-suspenders in view of the
- // above assert.
- NullOutListener();
- if (GetOwner()) {
- /* Call RemoveProxy with a successful status. This will keep the
- channel, if still downloading data, from being canceled if 'this' is
- the last observer. This allows the image to continue to download and
- be cached even if no one is using it currently.
- */
- mCanceled = true;
- GetOwner()->RemoveProxy(this, NS_OK);
- }
- }
- nsresult
- imgRequestProxy::Init(imgRequest* aOwner,
- nsILoadGroup* aLoadGroup,
- ImageURL* aURI,
- imgINotificationObserver* aObserver)
- {
- NS_PRECONDITION(!GetOwner() && !mListener,
- "imgRequestProxy is already initialized");
- LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request",
- aOwner);
- MOZ_ASSERT(mAnimationConsumers == 0, "Cannot have animation before Init");
- mBehaviour->SetOwner(aOwner);
- mListener = aObserver;
- // Make sure to addref mListener before the AddProxy call below, since
- // that call might well want to release it if the imgRequest has
- // already seen OnStopRequest.
- if (mListener) {
- mListenerIsStrongRef = true;
- NS_ADDREF(mListener);
- }
- mLoadGroup = aLoadGroup;
- mURI = aURI;
- // Note: AddProxy won't send all the On* notifications immediately
- if (GetOwner()) {
- GetOwner()->AddProxy(this);
- }
- return NS_OK;
- }
- nsresult
- imgRequestProxy::ChangeOwner(imgRequest* aNewOwner)
- {
- NS_PRECONDITION(GetOwner(),
- "Cannot ChangeOwner on a proxy without an owner!");
- if (mCanceled) {
- // Ensure that this proxy has received all notifications to date
- // before we clean it up when removing it from the old owner below.
- SyncNotifyListener();
- }
- // If we're holding locks, unlock the old image.
- // Note that UnlockImage decrements mLockCount each time it's called.
- uint32_t oldLockCount = mLockCount;
- while (mLockCount) {
- UnlockImage();
- }
- // If we're holding animation requests, undo them.
- uint32_t oldAnimationConsumers = mAnimationConsumers;
- ClearAnimationConsumers();
- GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
- mBehaviour->SetOwner(aNewOwner);
- // If we were locked, apply the locks here
- for (uint32_t i = 0; i < oldLockCount; i++) {
- LockImage();
- }
- // If we had animation requests, restore them here. Note that we
- // do this *after* RemoveProxy, which clears out animation consumers
- // (see bug 601723).
- for (uint32_t i = 0; i < oldAnimationConsumers; i++) {
- IncrementAnimationConsumers();
- }
- GetOwner()->AddProxy(this);
- // If we'd previously requested a synchronous decode, request a decode on the
- // new image.
- if (mDecodeRequested) {
- StartDecoding();
- }
- return NS_OK;
- }
- void
- imgRequestProxy::AddToLoadGroup()
- {
- NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
- if (!mIsInLoadGroup && mLoadGroup) {
- mLoadGroup->AddRequest(this, nullptr);
- mIsInLoadGroup = true;
- }
- }
- void
- imgRequestProxy::RemoveFromLoadGroup(bool releaseLoadGroup)
- {
- if (!mIsInLoadGroup) {
- return;
- }
- /* calling RemoveFromLoadGroup may cause the document to finish
- loading, which could result in our death. We need to make sure
- that we stay alive long enough to fight another battle... at
- least until we exit this function.
- */
- nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
- mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
- mIsInLoadGroup = false;
- if (releaseLoadGroup) {
- // We're done with the loadgroup, release it.
- mLoadGroup = nullptr;
- }
- }
- /** nsIRequest / imgIRequest methods **/
- NS_IMETHODIMP
- imgRequestProxy::GetName(nsACString& aName)
- {
- aName.Truncate();
- if (mURI) {
- mURI->GetSpec(aName);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::IsPending(bool* _retval)
- {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetStatus(nsresult* aStatus)
- {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- NS_IMETHODIMP
- imgRequestProxy::Cancel(nsresult status)
- {
- if (mCanceled) {
- return NS_ERROR_FAILURE;
- }
- LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
- mCanceled = true;
- nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
- return NS_DispatchToCurrentThread(ev);
- }
- void
- imgRequestProxy::DoCancel(nsresult status)
- {
- if (GetOwner()) {
- GetOwner()->RemoveProxy(this, status);
- }
- NullOutListener();
- }
- NS_IMETHODIMP
- imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
- {
- // If mCanceled is true but mListener is non-null, that means
- // someone called Cancel() on us but the imgCancelRunnable is still
- // pending. We still need to null out mListener before returning
- // from this function in this case. That means we want to do the
- // RemoveProxy call right now, because we need to deliver the
- // onStopRequest.
- if (mCanceled && !mListener) {
- return NS_ERROR_FAILURE;
- }
- LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
- mCanceled = true;
- // Now cheat and make sure our removal from loadgroup happens async
- bool oldIsInLoadGroup = mIsInLoadGroup;
- mIsInLoadGroup = false;
- if (GetOwner()) {
- GetOwner()->RemoveProxy(this, aStatus);
- }
- mIsInLoadGroup = oldIsInLoadGroup;
- if (mIsInLoadGroup) {
- NS_DispatchToCurrentThread(NewRunnableMethod(this, &imgRequestProxy::DoRemoveFromLoadGroup));
- }
- NullOutListener();
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::StartDecoding()
- {
- // Flag this, so we know to transfer the request if our owner changes
- mDecodeRequested = true;
- RefPtr<Image> image = GetImage();
- if (image) {
- return image->StartDecoding();
- }
- if (GetOwner()) {
- GetOwner()->StartDecoding();
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::LockImage()
- {
- mLockCount++;
- RefPtr<Image> image = GetImage();
- if (image) {
- return image->LockImage();
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::UnlockImage()
- {
- MOZ_ASSERT(mLockCount > 0, "calling unlock but no locks!");
- mLockCount--;
- RefPtr<Image> image = GetImage();
- if (image) {
- return image->UnlockImage();
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::RequestDiscard()
- {
- RefPtr<Image> image = GetImage();
- if (image) {
- return image->RequestDiscard();
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::IncrementAnimationConsumers()
- {
- mAnimationConsumers++;
- RefPtr<Image> image = GetImage();
- if (image) {
- image->IncrementAnimationConsumers();
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::DecrementAnimationConsumers()
- {
- // We may get here if some responsible code called Increment,
- // then called us, but we have meanwhile called ClearAnimationConsumers
- // because we needed to get rid of them earlier (see
- // imgRequest::RemoveProxy), and hence have nothing left to
- // decrement. (In such a case we got rid of the animation consumers
- // early, but not the observer.)
- if (mAnimationConsumers > 0) {
- mAnimationConsumers--;
- RefPtr<Image> image = GetImage();
- if (image) {
- image->DecrementAnimationConsumers();
- }
- }
- return NS_OK;
- }
- void
- imgRequestProxy::ClearAnimationConsumers()
- {
- while (mAnimationConsumers > 0) {
- DecrementAnimationConsumers();
- }
- }
- NS_IMETHODIMP
- imgRequestProxy::Suspend()
- {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- NS_IMETHODIMP
- imgRequestProxy::Resume()
- {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetLoadGroup(nsILoadGroup** loadGroup)
- {
- NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::SetLoadGroup(nsILoadGroup* loadGroup)
- {
- mLoadGroup = loadGroup;
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetLoadFlags(nsLoadFlags* flags)
- {
- *flags = mLoadFlags;
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::SetLoadFlags(nsLoadFlags flags)
- {
- mLoadFlags = flags;
- return NS_OK;
- }
- /** imgIRequest methods **/
- NS_IMETHODIMP
- imgRequestProxy::GetImage(imgIContainer** aImage)
- {
- NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
- // It's possible that our owner has an image but hasn't notified us of it -
- // that'll happen if we get Canceled before the owner instantiates its image
- // (because Canceling unregisters us as a listener on mOwner). If we're
- // in that situation, just grab the image off of mOwner.
- RefPtr<Image> image = GetImage();
- nsCOMPtr<imgIContainer> imageToReturn;
- if (image) {
- imageToReturn = do_QueryInterface(image);
- }
- if (!imageToReturn && GetOwner()) {
- imageToReturn = GetOwner()->GetImage();
- }
- if (!imageToReturn) {
- return NS_ERROR_FAILURE;
- }
- imageToReturn.swap(*aImage);
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetImageStatus(uint32_t* aStatus)
- {
- RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
- *aStatus = progressTracker->GetImageStatus();
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetImageErrorCode(nsresult* aStatus)
- {
- if (!GetOwner()) {
- return NS_ERROR_FAILURE;
- }
- *aStatus = GetOwner()->GetImageErrorCode();
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetURI(nsIURI** aURI)
- {
- MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
- nsCOMPtr<nsIURI> uri = mURI->ToIURI();
- uri.forget(aURI);
- return NS_OK;
- }
- nsresult
- imgRequestProxy::GetCurrentURI(nsIURI** aURI)
- {
- if (!GetOwner()) {
- return NS_ERROR_FAILURE;
- }
- return GetOwner()->GetCurrentURI(aURI);
- }
- nsresult
- imgRequestProxy::GetURI(ImageURL** aURI)
- {
- if (!mURI) {
- return NS_ERROR_FAILURE;
- }
- NS_ADDREF(*aURI = mURI);
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetNotificationObserver(imgINotificationObserver** aObserver)
- {
- *aObserver = mListener;
- NS_IF_ADDREF(*aObserver);
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetMimeType(char** aMimeType)
- {
- if (!GetOwner()) {
- return NS_ERROR_FAILURE;
- }
- const char* type = GetOwner()->GetMimeType();
- if (!type) {
- return NS_ERROR_FAILURE;
- }
- *aMimeType = NS_strdup(type);
- return NS_OK;
- }
- static imgRequestProxy* NewProxy(imgRequestProxy* /*aThis*/)
- {
- return new imgRequestProxy();
- }
- imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis)
- {
- nsCOMPtr<nsIPrincipal> currentPrincipal;
- aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal));
- RefPtr<Image> image = aThis->GetImage();
- return new imgRequestProxyStatic(image, currentPrincipal);
- }
- NS_IMETHODIMP
- imgRequestProxy::Clone(imgINotificationObserver* aObserver,
- imgIRequest** aClone)
- {
- nsresult result;
- imgRequestProxy* proxy;
- result = Clone(aObserver, &proxy);
- *aClone = proxy;
- return result;
- }
- nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
- imgRequestProxy** aClone)
- {
- return PerformClone(aObserver, NewProxy, aClone);
- }
- nsresult
- imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
- imgRequestProxy* (aAllocFn)(imgRequestProxy*),
- imgRequestProxy** aClone)
- {
- NS_PRECONDITION(aClone, "Null out param");
- LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
- *aClone = nullptr;
- RefPtr<imgRequestProxy> clone = aAllocFn(this);
- // It is important to call |SetLoadFlags()| before calling |Init()| because
- // |Init()| adds the request to the loadgroup.
- // When a request is added to a loadgroup, its load flags are merged
- // with the load flags of the loadgroup.
- // XXXldb That's not true anymore. Stuff from imgLoader adds the
- // request to the loadgroup.
- clone->SetLoadFlags(mLoadFlags);
- nsresult rv = clone->Init(mBehaviour->GetOwner(), mLoadGroup,
- mURI, aObserver);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (GetOwner() && GetOwner()->GetValidator()) {
- clone->SetNotificationsDeferred(true);
- GetOwner()->GetValidator()->AddProxy(clone);
- }
- // Assign to *aClone before calling Notify so that if the caller expects to
- // only be notified for requests it's already holding pointers to it won't be
- // surprised.
- NS_ADDREF(*aClone = clone);
- // This is wrong!!! We need to notify asynchronously, but there's code that
- // assumes that we don't. This will be fixed in bug 580466.
- clone->SyncNotifyListener();
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetImagePrincipal(nsIPrincipal** aPrincipal)
- {
- if (!GetOwner()) {
- return NS_ERROR_FAILURE;
- }
- nsCOMPtr<nsIPrincipal> principal = GetOwner()->GetPrincipal();
- principal.forget(aPrincipal);
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetMultipart(bool* aMultipart)
- {
- if (!GetOwner()) {
- return NS_ERROR_FAILURE;
- }
- *aMultipart = GetOwner()->GetMultipart();
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetCORSMode(int32_t* aCorsMode)
- {
- if (!GetOwner()) {
- return NS_ERROR_FAILURE;
- }
- *aCorsMode = GetOwner()->GetCORSMode();
- return NS_OK;
- }
- /** nsISupportsPriority methods **/
- NS_IMETHODIMP
- imgRequestProxy::GetPriority(int32_t* priority)
- {
- NS_ENSURE_STATE(GetOwner());
- *priority = GetOwner()->Priority();
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::SetPriority(int32_t priority)
- {
- NS_ENSURE_STATE(GetOwner() && !mCanceled);
- GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority());
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::AdjustPriority(int32_t priority)
- {
- // We don't require |!mCanceled| here. This may be called even if we're
- // cancelled, because it's invoked as part of the process of removing an image
- // from the load group.
- NS_ENSURE_STATE(GetOwner());
- GetOwner()->AdjustPriority(this, priority);
- return NS_OK;
- }
- /** nsISecurityInfoProvider methods **/
- NS_IMETHODIMP
- imgRequestProxy::GetSecurityInfo(nsISupports** _retval)
- {
- if (GetOwner()) {
- return GetOwner()->GetSecurityInfo(_retval);
- }
- *_retval = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- imgRequestProxy::GetHasTransferredData(bool* hasData)
- {
- if (GetOwner()) {
- *hasData = GetOwner()->HasTransferredData();
- } else {
- // The safe thing to do is to claim we have data
- *hasData = true;
- }
- return NS_OK;
- }
- static const char*
- NotificationTypeToString(int32_t aType)
- {
- switch(aType)
- {
- case imgINotificationObserver::SIZE_AVAILABLE: return "SIZE_AVAILABLE";
- case imgINotificationObserver::FRAME_UPDATE: return "FRAME_UPDATE";
- case imgINotificationObserver::FRAME_COMPLETE: return "FRAME_COMPLETE";
- case imgINotificationObserver::LOAD_COMPLETE: return "LOAD_COMPLETE";
- case imgINotificationObserver::DECODE_COMPLETE: return "DECODE_COMPLETE";
- case imgINotificationObserver::DISCARD: return "DISCARD";
- case imgINotificationObserver::UNLOCKED_DRAW: return "UNLOCKED_DRAW";
- case imgINotificationObserver::IS_ANIMATED: return "IS_ANIMATED";
- case imgINotificationObserver::HAS_TRANSPARENCY: return "HAS_TRANSPARENCY";
- default:
- NS_NOTREACHED("Notification list should be exhaustive");
- return "(unknown notification)";
- }
- }
- void
- imgRequestProxy::Notify(int32_t aType, const mozilla::gfx::IntRect* aRect)
- {
- MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE,
- "Should call OnLoadComplete");
- LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::Notify", "type",
- NotificationTypeToString(aType));
- if (!mListener || mCanceled) {
- return;
- }
- // Make sure the listener stays alive while we notify.
- nsCOMPtr<imgINotificationObserver> listener(mListener);
- listener->Notify(this, aType, aRect);
- }
- void
- imgRequestProxy::OnLoadComplete(bool aLastPart)
- {
- if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
- nsAutoCString name;
- GetName(name);
- LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnLoadComplete",
- "name", name.get());
- }
- // There's all sorts of stuff here that could kill us (the OnStopRequest call
- // on the listener, the removal from the loadgroup, the release of the
- // listener, etc). Don't let them do it.
- nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
- if (mListener && !mCanceled) {
- // Hold a ref to the listener while we call it, just in case.
- nsCOMPtr<imgINotificationObserver> listener(mListener);
- listener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
- }
- // If we're expecting more data from a multipart channel, re-add ourself
- // to the loadgroup so that the document doesn't lose track of the load.
- // If the request is already a background request and there's more data
- // coming, we can just leave the request in the loadgroup as-is.
- if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
- RemoveFromLoadGroup(aLastPart);
- // More data is coming, so change the request to be a background request
- // and put it back in the loadgroup.
- if (!aLastPart) {
- mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
- AddToLoadGroup();
- }
- }
- if (mListenerIsStrongRef && aLastPart) {
- NS_PRECONDITION(mListener, "How did that happen?");
- // Drop our strong ref to the listener now that we're done with
- // everything. Note that this can cancel us and other fun things
- // like that. Don't add anything in this method after this point.
- imgINotificationObserver* obs = mListener;
- mListenerIsStrongRef = false;
- NS_RELEASE(obs);
- }
- }
- void
- imgRequestProxy::BlockOnload()
- {
- if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
- nsAutoCString name;
- GetName(name);
- LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::BlockOnload",
- "name", name.get());
- }
- nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
- if (blocker) {
- blocker->BlockOnload(this);
- }
- }
- void
- imgRequestProxy::UnblockOnload()
- {
- if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
- nsAutoCString name;
- GetName(name);
- LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::UnblockOnload",
- "name", name.get());
- }
- nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
- if (blocker) {
- blocker->UnblockOnload(this);
- }
- }
- void
- imgRequestProxy::NullOutListener()
- {
- // If we have animation consumers, then they don't matter anymore
- if (mListener) {
- ClearAnimationConsumers();
- }
- if (mListenerIsStrongRef) {
- // Releasing could do weird reentery stuff, so just play it super-safe
- nsCOMPtr<imgINotificationObserver> obs;
- obs.swap(mListener);
- mListenerIsStrongRef = false;
- } else {
- mListener = nullptr;
- }
- }
- NS_IMETHODIMP
- imgRequestProxy::GetStaticRequest(imgIRequest** aReturn)
- {
- imgRequestProxy* proxy;
- nsresult result = GetStaticRequest(&proxy);
- *aReturn = proxy;
- return result;
- }
- nsresult
- imgRequestProxy::GetStaticRequest(imgRequestProxy** aReturn)
- {
- *aReturn = nullptr;
- RefPtr<Image> image = GetImage();
- bool animated;
- if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
- // Early exit - we're not animated, so we don't have to do anything.
- NS_ADDREF(*aReturn = this);
- return NS_OK;
- }
- // Check for errors in the image. Callers code rely on GetStaticRequest
- // failing in this case, though with FrozenImage there's no technical reason
- // for it anymore.
- if (image->HasError()) {
- return NS_ERROR_FAILURE;
- }
- // We are animated. We need to create a frozen version of this image.
- RefPtr<Image> frozenImage = ImageOps::Freeze(image);
- // Create a static imgRequestProxy with our new extracted frame.
- nsCOMPtr<nsIPrincipal> currentPrincipal;
- GetImagePrincipal(getter_AddRefs(currentPrincipal));
- RefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frozenImage,
- currentPrincipal);
- req->Init(nullptr, nullptr, mURI, nullptr);
- NS_ADDREF(*aReturn = req);
- return NS_OK;
- }
- void
- imgRequestProxy::NotifyListener()
- {
- // It would be nice to notify the observer directly in the status tracker
- // instead of through the proxy, but there are several places we do extra
- // processing when we receive notifications (like OnStopRequest()), and we
- // need to check mCanceled everywhere too.
- RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
- if (GetOwner()) {
- // Send the notifications to our listener asynchronously.
- progressTracker->Notify(this);
- } else {
- // We don't have an imgRequest, so we can only notify the clone of our
- // current state, but we still have to do that asynchronously.
- MOZ_ASSERT(HasImage(),
- "if we have no imgRequest, we should have an Image");
- progressTracker->NotifyCurrentState(this);
- }
- }
- void
- imgRequestProxy::SyncNotifyListener()
- {
- // It would be nice to notify the observer directly in the status tracker
- // instead of through the proxy, but there are several places we do extra
- // processing when we receive notifications (like OnStopRequest()), and we
- // need to check mCanceled everywhere too.
- RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
- progressTracker->SyncNotify(this);
- }
- void
- imgRequestProxy::SetHasImage()
- {
- RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
- MOZ_ASSERT(progressTracker);
- RefPtr<Image> image = progressTracker->GetImage();
- MOZ_ASSERT(image);
- // Force any private status related to the owner to reflect
- // the presence of an image;
- mBehaviour->SetOwner(mBehaviour->GetOwner());
- // Apply any locks we have
- for (uint32_t i = 0; i < mLockCount; ++i) {
- image->LockImage();
- }
- // Apply any animation consumers we have
- for (uint32_t i = 0; i < mAnimationConsumers; i++) {
- image->IncrementAnimationConsumers();
- }
- }
- already_AddRefed<ProgressTracker>
- imgRequestProxy::GetProgressTracker() const
- {
- return mBehaviour->GetProgressTracker();
- }
- already_AddRefed<mozilla::image::Image>
- imgRequestProxy::GetImage() const
- {
- return mBehaviour->GetImage();
- }
- bool
- RequestBehaviour::HasImage() const
- {
- if (!mOwnerHasImage) {
- return false;
- }
- RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
- return progressTracker ? progressTracker->HasImage() : false;
- }
- bool
- imgRequestProxy::HasImage() const
- {
- return mBehaviour->HasImage();
- }
- imgRequest*
- imgRequestProxy::GetOwner() const
- {
- return mBehaviour->GetOwner();
- }
- ////////////////// imgRequestProxyStatic methods
- class StaticBehaviour : public ProxyBehaviour
- {
- public:
- explicit StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
- virtual already_AddRefed<mozilla::image::Image>
- GetImage() const override {
- RefPtr<mozilla::image::Image> image = mImage;
- return image.forget();
- }
- virtual bool HasImage() const override {
- return mImage;
- }
- virtual already_AddRefed<ProgressTracker> GetProgressTracker()
- const override {
- return mImage->GetProgressTracker();
- }
- virtual imgRequest* GetOwner() const override {
- return nullptr;
- }
- virtual void SetOwner(imgRequest* aOwner) override {
- MOZ_ASSERT(!aOwner,
- "We shouldn't be giving static requests a non-null owner.");
- }
- private:
- // Our image. We have to hold a strong reference here, because that's normally
- // the job of the underlying request.
- RefPtr<mozilla::image::Image> mImage;
- };
- imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage,
- nsIPrincipal* aPrincipal)
- : mPrincipal(aPrincipal)
- {
- mBehaviour = mozilla::MakeUnique<StaticBehaviour>(aImage);
- }
- NS_IMETHODIMP
- imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal** aPrincipal)
- {
- if (!mPrincipal) {
- return NS_ERROR_FAILURE;
- }
- NS_ADDREF(*aPrincipal = mPrincipal);
- return NS_OK;
- }
- nsresult
- imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver,
- imgRequestProxy** aClone)
- {
- return PerformClone(aObserver, NewStaticProxy, aClone);
- }
|