mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
1509ba1c86
Differential Revision: https://phabricator.services.mozilla.com/D172920
1328 lines
41 KiB
C++
1328 lines
41 KiB
C++
/* -*- 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 "imgRequestProxy.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "Image.h"
|
|
#include "ImageLogging.h"
|
|
#include "ImageOps.h"
|
|
#include "ImageTypes.h"
|
|
#include "imgINotificationObserver.h"
|
|
#include "imgLoader.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/Telemetry.h" // for Telemetry
|
|
#include "mozilla/dom/DocGroup.h" // for DocGroup
|
|
#include "nsCRTGlue.h"
|
|
#include "nsError.h"
|
|
|
|
using namespace mozilla;
|
|
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() = default;
|
|
|
|
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) {}
|
|
|
|
already_AddRefed<mozilla::image::Image> GetImage() const override;
|
|
bool HasImage() const override;
|
|
already_AddRefed<ProgressTracker> GetProgressTracker() const override;
|
|
|
|
imgRequest* GetOwner() const override { return mOwner; }
|
|
|
|
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, PreloaderBase)
|
|
NS_INTERFACE_MAP_ENTRY(imgIRequest)
|
|
NS_INTERFACE_MAP_ENTRY(nsIRequest)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
|
|
NS_INTERFACE_MAP_ENTRY_CONCRETE(imgRequestProxy)
|
|
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),
|
|
mCancelable(true),
|
|
mCanceled(false),
|
|
mIsInLoadGroup(false),
|
|
mForceDispatchLoadGroup(false),
|
|
mListenerIsStrongRef(false),
|
|
mDecodeRequested(false),
|
|
mPendingNotify(false),
|
|
mValidating(false),
|
|
mHadListener(false),
|
|
mHadDispatch(false) {
|
|
/* member initializers and constructor code */
|
|
LOG_FUNC(gImgLog, "imgRequestProxy::imgRequestProxy");
|
|
}
|
|
|
|
imgRequestProxy::~imgRequestProxy() {
|
|
/* destructor code */
|
|
MOZ_ASSERT(!mListener, "Someone forgot to properly cancel this request!");
|
|
|
|
// If we had a listener, that means we would have issued notifications. With
|
|
// bug 1359833, we added support for main thread scheduler groups. Each
|
|
// imgRequestProxy may have its own associated listener, document and/or
|
|
// scheduler group. Typically most imgRequestProxy belong to the same
|
|
// document, or have no listener, which means we will want to execute all main
|
|
// thread code in that shared scheduler group. Less frequently, there may be
|
|
// multiple imgRequests and they have separate documents, which means that
|
|
// when we issue state notifications, some or all need to be dispatched to the
|
|
// appropriate scheduler group for each request. This should be rare, so we
|
|
// want to monitor the frequency of dispatching in the wild.
|
|
if (mHadListener) {
|
|
mozilla::Telemetry::Accumulate(mozilla::Telemetry::IMAGE_REQUEST_DISPATCHED,
|
|
mHadDispatch);
|
|
}
|
|
|
|
MOZ_RELEASE_ASSERT(!mLockCount, "Someone forgot to unlock on time?");
|
|
|
|
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();
|
|
|
|
/* 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;
|
|
RemoveFromOwner(NS_OK);
|
|
|
|
RemoveFromLoadGroup();
|
|
LOG_FUNC(gImgLog, "imgRequestProxy::~imgRequestProxy");
|
|
}
|
|
|
|
nsresult imgRequestProxy::Init(imgRequest* aOwner, nsILoadGroup* aLoadGroup,
|
|
Document* aLoadingDocument, nsIURI* aURI,
|
|
imgINotificationObserver* aObserver) {
|
|
MOZ_ASSERT(!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 AddToOwner call below, since
|
|
// that call might well want to release it if the imgRequest has
|
|
// already seen OnStopRequest.
|
|
if (mListener) {
|
|
mHadListener = true;
|
|
mListenerIsStrongRef = true;
|
|
NS_ADDREF(mListener);
|
|
}
|
|
mLoadGroup = aLoadGroup;
|
|
mURI = aURI;
|
|
|
|
// Note: AddToOwner won't send all the On* notifications immediately
|
|
AddToOwner(aLoadingDocument);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult imgRequestProxy::ChangeOwner(imgRequest* aNewOwner) {
|
|
MOZ_ASSERT(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_OK);
|
|
|
|
mBehaviour->SetOwner(aNewOwner);
|
|
MOZ_ASSERT(!GetValidator(), "New owner cannot be validating!");
|
|
|
|
// 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();
|
|
}
|
|
|
|
AddToOwner(nullptr);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP imgRequestProxy::GetTriggeringPrincipal(
|
|
nsIPrincipal** aTriggeringPrincipal) {
|
|
MOZ_ASSERT(GetOwner());
|
|
nsCOMPtr<nsIPrincipal> triggeringPrincipal =
|
|
GetOwner()->GetTriggeringPrincipal();
|
|
triggeringPrincipal.forget(aTriggeringPrincipal);
|
|
return NS_OK;
|
|
}
|
|
|
|
void imgRequestProxy::MarkValidating() {
|
|
MOZ_ASSERT(GetValidator());
|
|
mValidating = true;
|
|
}
|
|
|
|
void imgRequestProxy::ClearValidating() {
|
|
MOZ_ASSERT(mValidating);
|
|
MOZ_ASSERT(!GetValidator());
|
|
mValidating = false;
|
|
|
|
// If we'd previously requested a synchronous decode, request a decode on the
|
|
// new image.
|
|
if (mDecodeRequested) {
|
|
mDecodeRequested = false;
|
|
StartDecoding(imgIContainer::FLAG_NONE);
|
|
}
|
|
}
|
|
|
|
already_AddRefed<nsIEventTarget> imgRequestProxy::GetEventTarget() const {
|
|
nsCOMPtr<nsIEventTarget> target(mEventTarget);
|
|
return target.forget();
|
|
}
|
|
|
|
bool imgRequestProxy::HasDecodedPixels() {
|
|
if (IsValidating()) {
|
|
return false;
|
|
}
|
|
|
|
RefPtr<Image> image = GetImage();
|
|
if (image) {
|
|
return image->HasDecodedPixels();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
nsresult imgRequestProxy::DispatchWithTargetIfAvailable(
|
|
already_AddRefed<nsIRunnable> aEvent) {
|
|
LOG_FUNC(gImgLog, "imgRequestProxy::DispatchWithTargetIfAvailable");
|
|
|
|
// This method should only be used when it is *expected* that we are
|
|
// dispatching an event (e.g. we want to handle an event asynchronously)
|
|
// rather we need to (e.g. we are in the wrong scheduler group context).
|
|
// As such, we do not set mHadDispatch for telemetry purposes.
|
|
if (mEventTarget) {
|
|
mEventTarget->Dispatch(CreateRenderBlockingRunnable(std::move(aEvent)),
|
|
NS_DISPATCH_NORMAL);
|
|
return NS_OK;
|
|
}
|
|
|
|
return NS_DispatchToMainThread(
|
|
CreateRenderBlockingRunnable(std::move(aEvent)));
|
|
}
|
|
|
|
void imgRequestProxy::AddToOwner(Document* aLoadingDocument) {
|
|
// An imgRequestProxy can be initialized with neither a listener nor a
|
|
// document. The caller could follow up later by cloning the canonical
|
|
// imgRequestProxy with the actual listener. This is possible because
|
|
// imgLoader::LoadImage does not require a valid listener to be provided.
|
|
//
|
|
// Without a listener, we don't need to set our scheduler group, because
|
|
// we have nothing to signal. However if we were told what document this
|
|
// is for, it is likely that future listeners will belong to the same
|
|
// scheduler group.
|
|
//
|
|
// With a listener, we always need to update our scheduler group. A null
|
|
// scheduler group is valid with or without a document, but that means
|
|
// we will use the most generic event target possible on dispatch.
|
|
if (aLoadingDocument) {
|
|
RefPtr<mozilla::dom::DocGroup> docGroup = aLoadingDocument->GetDocGroup();
|
|
if (docGroup) {
|
|
mEventTarget = docGroup->EventTargetFor(mozilla::TaskCategory::Other);
|
|
MOZ_ASSERT(mEventTarget);
|
|
}
|
|
}
|
|
|
|
if (mListener && !mEventTarget) {
|
|
mEventTarget = do_GetMainThread();
|
|
}
|
|
|
|
imgRequest* owner = GetOwner();
|
|
if (!owner) {
|
|
return;
|
|
}
|
|
|
|
owner->AddProxy(this);
|
|
}
|
|
|
|
void imgRequestProxy::RemoveFromOwner(nsresult aStatus) {
|
|
imgRequest* owner = GetOwner();
|
|
if (owner) {
|
|
if (mValidating) {
|
|
imgCacheValidator* validator = owner->GetValidator();
|
|
MOZ_ASSERT(validator);
|
|
validator->RemoveProxy(this);
|
|
mValidating = false;
|
|
}
|
|
|
|
owner->RemoveProxy(this, aStatus);
|
|
}
|
|
}
|
|
|
|
void imgRequestProxy::AddToLoadGroup() {
|
|
NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
|
|
MOZ_ASSERT(!mForceDispatchLoadGroup);
|
|
|
|
/* While in theory there could be a dispatch outstanding to remove this
|
|
request from the load group, in practice we only add to the load group
|
|
(when previously not in a load group) at initialization. */
|
|
if (!mIsInLoadGroup && mLoadGroup) {
|
|
LOG_FUNC(gImgLog, "imgRequestProxy::AddToLoadGroup");
|
|
mLoadGroup->AddRequest(this, nullptr);
|
|
mIsInLoadGroup = true;
|
|
}
|
|
}
|
|
|
|
void imgRequestProxy::RemoveFromLoadGroup() {
|
|
if (!mIsInLoadGroup || !mLoadGroup) {
|
|
return;
|
|
}
|
|
|
|
/* Sometimes we may not be able to remove ourselves from the load group in
|
|
the current context. This is because our listeners are not re-entrant (e.g.
|
|
we are in the middle of CancelAndForgetObserver or SyncClone). */
|
|
if (mForceDispatchLoadGroup) {
|
|
LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup -- dispatch");
|
|
|
|
/* We take away the load group from the request temporarily; this prevents
|
|
additional dispatches via RemoveFromLoadGroup occurring, as well as
|
|
MoveToBackgroundInLoadGroup from removing and readding. This is safe
|
|
because we know that once we get here, blocking the load group at all is
|
|
unnecessary. */
|
|
mIsInLoadGroup = false;
|
|
nsCOMPtr<nsILoadGroup> loadGroup = std::move(mLoadGroup);
|
|
RefPtr<imgRequestProxy> self(this);
|
|
DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
|
|
"imgRequestProxy::RemoveFromLoadGroup", [self, loadGroup]() -> void {
|
|
loadGroup->RemoveRequest(self, nullptr, NS_OK);
|
|
}));
|
|
return;
|
|
}
|
|
|
|
LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup");
|
|
|
|
/* 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);
|
|
mLoadGroup = nullptr;
|
|
mIsInLoadGroup = false;
|
|
}
|
|
|
|
void imgRequestProxy::MoveToBackgroundInLoadGroup() {
|
|
/* Even if we are still in the load group, we may have taken away the load
|
|
group reference itself because we are in the process of leaving the group.
|
|
In that case, there is no need to background the request. */
|
|
if (!mLoadGroup) {
|
|
return;
|
|
}
|
|
|
|
/* There is no need to dispatch if we only need to add ourselves to the load
|
|
group without removal. It is the removal which causes the problematic
|
|
callbacks (see RemoveFromLoadGroup). */
|
|
if (mIsInLoadGroup && mForceDispatchLoadGroup) {
|
|
LOG_FUNC(gImgLog,
|
|
"imgRequestProxy::MoveToBackgroundInLoadGroup -- dispatch");
|
|
|
|
RefPtr<imgRequestProxy> self(this);
|
|
DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
|
|
"imgRequestProxy::MoveToBackgroundInLoadGroup",
|
|
[self]() -> void { self->MoveToBackgroundInLoadGroup(); }));
|
|
return;
|
|
}
|
|
|
|
LOG_FUNC(gImgLog, "imgRequestProxy::MoveToBackgroundInLoadGroup");
|
|
nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
|
|
if (mIsInLoadGroup) {
|
|
mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
|
|
}
|
|
|
|
mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
|
|
mLoadGroup->AddRequest(this, 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::SetCanceledReason(const nsACString& aReason) {
|
|
return SetCanceledReasonImpl(aReason);
|
|
}
|
|
|
|
NS_IMETHODIMP imgRequestProxy::GetCanceledReason(nsACString& aReason) {
|
|
return GetCanceledReasonImpl(aReason);
|
|
}
|
|
|
|
NS_IMETHODIMP imgRequestProxy::CancelWithReason(nsresult aStatus,
|
|
const nsACString& aReason) {
|
|
return CancelWithReasonImpl(aStatus, aReason);
|
|
}
|
|
|
|
void imgRequestProxy::SetCancelable(bool aCancelable) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
mCancelable = aCancelable;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::Cancel(nsresult status) {
|
|
if (mCanceled) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (NS_WARN_IF(!mCancelable)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
|
|
|
|
mCanceled = true;
|
|
|
|
nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
|
|
return DispatchWithTargetIfAvailable(ev.forget());
|
|
}
|
|
|
|
void imgRequestProxy::DoCancel(nsresult status) {
|
|
RemoveFromOwner(status);
|
|
RemoveFromLoadGroup();
|
|
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;
|
|
}
|
|
|
|
if (NS_WARN_IF(!mCancelable)) {
|
|
MOZ_ASSERT(mCancelable,
|
|
"Shouldn't try to cancel non-cancelable requests via "
|
|
"CancelAndForgetObserver");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
|
|
|
|
mCanceled = true;
|
|
mForceDispatchLoadGroup = true;
|
|
RemoveFromOwner(aStatus);
|
|
RemoveFromLoadGroup();
|
|
mForceDispatchLoadGroup = false;
|
|
|
|
NullOutListener();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::StartDecoding(uint32_t aFlags) {
|
|
// Flag this, so we know to request after validation if pending.
|
|
if (IsValidating()) {
|
|
mDecodeRequested = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<Image> image = GetImage();
|
|
if (image) {
|
|
return image->StartDecoding(aFlags);
|
|
}
|
|
|
|
if (GetOwner()) {
|
|
GetOwner()->StartDecoding();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool imgRequestProxy::StartDecodingWithResult(uint32_t aFlags) {
|
|
// Flag this, so we know to request after validation if pending.
|
|
if (IsValidating()) {
|
|
mDecodeRequested = true;
|
|
return false;
|
|
}
|
|
|
|
RefPtr<Image> image = GetImage();
|
|
if (image) {
|
|
return image->StartDecodingWithResult(aFlags);
|
|
}
|
|
|
|
if (GetOwner()) {
|
|
GetOwner()->StartDecoding();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
imgIContainer::DecodeResult imgRequestProxy::RequestDecodeWithResult(
|
|
uint32_t aFlags) {
|
|
if (IsValidating()) {
|
|
mDecodeRequested = true;
|
|
return imgIContainer::DECODE_REQUESTED;
|
|
}
|
|
|
|
RefPtr<Image> image = GetImage();
|
|
if (image) {
|
|
return image->RequestDecodeWithResult(aFlags);
|
|
}
|
|
|
|
if (GetOwner()) {
|
|
GetOwner()->StartDecoding();
|
|
}
|
|
|
|
return imgIContainer::DECODE_REQUESTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::LockImage() {
|
|
mLockCount++;
|
|
RefPtr<Image> image =
|
|
GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
|
|
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 =
|
|
GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
|
|
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 =
|
|
GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
|
|
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 =
|
|
GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
|
|
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) {
|
|
if (loadGroup != mLoadGroup) {
|
|
MOZ_ASSERT_UNREACHABLE("Switching load groups is unsupported!");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
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;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
|
|
return GetTRRModeImpl(aTRRMode);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
|
|
return SetTRRModeImpl(aTRRMode);
|
|
}
|
|
|
|
/** 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 = image;
|
|
}
|
|
if (!imageToReturn && GetOwner()) {
|
|
imageToReturn = GetOwner()->GetImage();
|
|
}
|
|
if (!imageToReturn) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
imageToReturn.swap(*aImage);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::GetProviderId(uint32_t* aId) {
|
|
NS_ENSURE_TRUE(aId, NS_ERROR_NULL_POINTER);
|
|
|
|
nsCOMPtr<imgIContainer> image;
|
|
nsresult rv = GetImage(getter_AddRefs(image));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
*aId = image->GetProviderId();
|
|
} else {
|
|
*aId = 0;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::GetImageStatus(uint32_t* aStatus) {
|
|
if (IsValidating()) {
|
|
// We are currently validating the image, and so our status could revert if
|
|
// we discard the cache. We should also be deferring notifications, such
|
|
// that the caller will be notified when validation completes. Rather than
|
|
// risk misleading the caller, return nothing.
|
|
*aStatus = imgIRequest::STATUS_NONE;
|
|
} else {
|
|
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;
|
|
uri.forget(aURI);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult imgRequestProxy::GetFinalURI(nsIURI** aURI) {
|
|
if (!GetOwner()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return GetOwner()->GetFinalURI(aURI);
|
|
}
|
|
|
|
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_xstrdup(type);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::GetFileName(nsACString& aFileName) {
|
|
if (!GetOwner()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
GetOwner()->GetFileName(aFileName);
|
|
return NS_OK;
|
|
}
|
|
|
|
imgRequestProxy* imgRequestProxy::NewClonedProxy() {
|
|
return new imgRequestProxy();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::Clone(imgINotificationObserver* aObserver,
|
|
imgIRequest** aClone) {
|
|
nsresult result;
|
|
imgRequestProxy* proxy;
|
|
result = PerformClone(aObserver, nullptr, /* aSyncNotify */ true, &proxy);
|
|
*aClone = proxy;
|
|
return result;
|
|
}
|
|
|
|
nsresult imgRequestProxy::SyncClone(imgINotificationObserver* aObserver,
|
|
Document* aLoadingDocument,
|
|
imgRequestProxy** aClone) {
|
|
return PerformClone(aObserver, aLoadingDocument,
|
|
/* aSyncNotify */ true, aClone);
|
|
}
|
|
|
|
nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
|
|
Document* aLoadingDocument,
|
|
imgRequestProxy** aClone) {
|
|
return PerformClone(aObserver, aLoadingDocument,
|
|
/* aSyncNotify */ false, aClone);
|
|
}
|
|
|
|
nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
|
|
Document* aLoadingDocument,
|
|
bool aSyncNotify,
|
|
imgRequestProxy** aClone) {
|
|
MOZ_ASSERT(aClone, "Null out param");
|
|
|
|
LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
|
|
|
|
*aClone = nullptr;
|
|
RefPtr<imgRequestProxy> clone = NewClonedProxy();
|
|
|
|
nsCOMPtr<nsILoadGroup> loadGroup;
|
|
if (aLoadingDocument) {
|
|
loadGroup = aLoadingDocument->GetDocumentLoadGroup();
|
|
}
|
|
|
|
// 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(), loadGroup, aLoadingDocument,
|
|
mURI, aObserver);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// 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);
|
|
|
|
imgCacheValidator* validator = GetValidator();
|
|
if (validator) {
|
|
// Note that if we have a validator, we don't want to issue notifications at
|
|
// here because we want to defer until that completes. AddProxy will add us
|
|
// to the load group; we cannot avoid that in this case, because we don't
|
|
// know when the validation will complete, and if it will cause us to
|
|
// discard our cached state anyways. We are probably already blocked by the
|
|
// original LoadImage(WithChannel) request in any event.
|
|
clone->MarkValidating();
|
|
validator->AddProxy(clone);
|
|
} else {
|
|
// We only want to add the request to the load group of the owning document
|
|
// if it is still in progress. Some callers cannot handle a supurious load
|
|
// group removal (e.g. print preview) so we must be careful. On the other
|
|
// hand, if after cloning, the original request proxy is cancelled /
|
|
// destroyed, we need to ensure that any clones still block the load group
|
|
// if it is incomplete.
|
|
bool addToLoadGroup = mIsInLoadGroup;
|
|
if (!addToLoadGroup) {
|
|
RefPtr<ProgressTracker> tracker = clone->GetProgressTracker();
|
|
addToLoadGroup =
|
|
tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE);
|
|
}
|
|
|
|
if (addToLoadGroup) {
|
|
clone->AddToLoadGroup();
|
|
}
|
|
|
|
if (aSyncNotify) {
|
|
// 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. Note that
|
|
// if we have a validator, we won't issue notifications anyways because
|
|
// they are deferred, so there is no point in requesting.
|
|
clone->mForceDispatchLoadGroup = true;
|
|
clone->SyncNotifyListener();
|
|
clone->mForceDispatchLoadGroup = false;
|
|
} else {
|
|
// Without a validator, we can request asynchronous notifications
|
|
// immediately. If there was a validator, this would override the deferral
|
|
// and that would be incorrect.
|
|
clone->NotifyListener();
|
|
}
|
|
}
|
|
|
|
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::GetHadCrossOriginRedirects(bool* aHadCrossOriginRedirects) {
|
|
*aHadCrossOriginRedirects = false;
|
|
|
|
nsCOMPtr<nsITimedChannel> timedChannel = TimedChannel();
|
|
if (timedChannel) {
|
|
bool allRedirectsSameOrigin = false;
|
|
*aHadCrossOriginRedirects =
|
|
NS_SUCCEEDED(
|
|
timedChannel->GetAllRedirectsSameOrigin(&allRedirectsSameOrigin)) &&
|
|
!allRedirectsSameOrigin;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
|
|
if (!GetOwner()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIReferrerInfo> referrerInfo = GetOwner()->GetReferrerInfo();
|
|
referrerInfo.forget(aReferrerInfo);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxy::BoostPriority(uint32_t aCategory) {
|
|
NS_ENSURE_STATE(GetOwner() && !mCanceled);
|
|
GetOwner()->BoostPriority(aCategory);
|
|
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;
|
|
}
|
|
|
|
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:
|
|
MOZ_ASSERT_UNREACHABLE("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) {
|
|
LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnLoadComplete", "uri", mURI);
|
|
|
|
// 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.
|
|
RefPtr<imgRequestProxy> self(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) {
|
|
if (aLastPart) {
|
|
RemoveFromLoadGroup();
|
|
|
|
nsresult errorCode = NS_OK;
|
|
// if the load is cross origin without CORS, or the CORS access is
|
|
// rejected, always fire load event to avoid leaking site information for
|
|
// <link rel=preload>.
|
|
// XXXedgar, currently we don't do the same thing for <img>.
|
|
imgRequest* request = GetOwner();
|
|
if (!request || !(request->IsDeniedCrossSiteCORSRequest() ||
|
|
request->IsCrossSiteNoCORSRequest())) {
|
|
uint32_t status = imgIRequest::STATUS_NONE;
|
|
GetImageStatus(&status);
|
|
if (status & imgIRequest::STATUS_ERROR) {
|
|
errorCode = NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
NotifyStop(errorCode);
|
|
} else {
|
|
// More data is coming, so change the request to be a background request
|
|
// and put it back in the loadgroup.
|
|
MoveToBackgroundInLoadGroup();
|
|
}
|
|
}
|
|
|
|
if (mListenerIsStrongRef && aLastPart) {
|
|
MOZ_ASSERT(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::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) {
|
|
RefPtr<imgRequestProxy> proxy =
|
|
GetStaticRequest(static_cast<Document*>(nullptr));
|
|
if (proxy != this) {
|
|
RefPtr<Image> image = GetImage();
|
|
if (image && image->HasError()) {
|
|
// image/test/unit/test_async_notification_404.js needs this, but ideally
|
|
// this special case can be removed from the scripted codepath.
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
proxy.forget(aReturn);
|
|
return NS_OK;
|
|
}
|
|
|
|
already_AddRefed<imgRequestProxy> imgRequestProxy::GetStaticRequest(
|
|
Document* aLoadingDocument) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!aLoadingDocument ||
|
|
aLoadingDocument->IsStaticDocument());
|
|
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.
|
|
return do_AddRef(this);
|
|
}
|
|
|
|
// 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));
|
|
bool hadCrossOriginRedirects = true;
|
|
GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
|
|
nsCOMPtr<nsIPrincipal> triggeringPrincipal = GetTriggeringPrincipal();
|
|
RefPtr<imgRequestProxy> req =
|
|
new imgRequestProxyStatic(frozenImage, currentPrincipal,
|
|
triggeringPrincipal, hadCrossOriginRedirects);
|
|
req->Init(nullptr, nullptr, aLoadingDocument, mURI, nullptr);
|
|
|
|
return req.forget();
|
|
}
|
|
|
|
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(); }
|
|
|
|
imgCacheValidator* imgRequestProxy::GetValidator() const {
|
|
imgRequest* owner = GetOwner();
|
|
if (!owner) {
|
|
return nullptr;
|
|
}
|
|
return owner->GetValidator();
|
|
}
|
|
|
|
nsITimedChannel* imgRequestProxy::TimedChannel() {
|
|
if (!GetOwner()) {
|
|
return nullptr;
|
|
}
|
|
return GetOwner()->GetTimedChannel();
|
|
}
|
|
|
|
////////////////// imgRequestProxyStatic methods
|
|
|
|
class StaticBehaviour : public ProxyBehaviour {
|
|
public:
|
|
explicit StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
|
|
|
|
already_AddRefed<mozilla::image::Image> GetImage() const override {
|
|
RefPtr<mozilla::image::Image> image = mImage;
|
|
return image.forget();
|
|
}
|
|
|
|
bool HasImage() const override { return mImage; }
|
|
|
|
already_AddRefed<ProgressTracker> GetProgressTracker() const override {
|
|
return mImage->GetProgressTracker();
|
|
}
|
|
|
|
imgRequest* GetOwner() const override { return nullptr; }
|
|
|
|
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* aImagePrincipal,
|
|
nsIPrincipal* aTriggeringPrincipal,
|
|
bool aHadCrossOriginRedirects)
|
|
: mImagePrincipal(aImagePrincipal),
|
|
mTriggeringPrincipal(aTriggeringPrincipal),
|
|
mHadCrossOriginRedirects(aHadCrossOriginRedirects) {
|
|
mBehaviour = mozilla::MakeUnique<StaticBehaviour>(aImage);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal** aPrincipal) {
|
|
if (!mImagePrincipal) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
NS_ADDREF(*aPrincipal = mImagePrincipal);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxyStatic::GetTriggeringPrincipal(nsIPrincipal** aPrincipal) {
|
|
NS_IF_ADDREF(*aPrincipal = mTriggeringPrincipal);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
imgRequestProxyStatic::GetHadCrossOriginRedirects(
|
|
bool* aHadCrossOriginRedirects) {
|
|
*aHadCrossOriginRedirects = mHadCrossOriginRedirects;
|
|
return NS_OK;
|
|
}
|
|
|
|
imgRequestProxy* imgRequestProxyStatic::NewClonedProxy() {
|
|
nsCOMPtr<nsIPrincipal> currentPrincipal;
|
|
GetImagePrincipal(getter_AddRefs(currentPrincipal));
|
|
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
|
|
GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
|
|
bool hadCrossOriginRedirects = true;
|
|
GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
|
|
RefPtr<mozilla::image::Image> image = GetImage();
|
|
return new imgRequestProxyStatic(image, currentPrincipal, triggeringPrincipal,
|
|
hadCrossOriginRedirects);
|
|
}
|