Skip to content
This repository has been archived by the owner on Jan 12, 2022. It is now read-only.

Commit

Permalink
Bug 733553 - Allow multipart image streams to cope with stream change…
Browse files Browse the repository at this point in the history
…s. r=joe
  • Loading branch information
hobophobe committed May 9, 2012
1 parent a2769c4 commit 30d7a6f
Show file tree
Hide file tree
Showing 13 changed files with 84 additions and 33 deletions.
3 changes: 1 addition & 2 deletions image/decoders/nsBMPDecoder.cpp
Expand Up @@ -278,8 +278,7 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
// Post our size to the superclass
PostSize(mBIH.width, real_height);
if (HasError()) {
// Setting the size lead to an error; this can happen when for example
// a multipart channel sends an image of a different size.
// Setting the size led to an error.
return;
}

Expand Down
3 changes: 1 addition & 2 deletions image/decoders/nsGIFDecoder2.cpp
Expand Up @@ -920,8 +920,7 @@ nsGIFDecoder2::WriteInternal(const char *aBuffer, PRUint32 aCount)
// Create the image container with the right size.
BeginGIF();
if (HasError()) {
// Setting the size lead to an error; this can happen when for example
// a multipart channel sends an image of a different size.
// Setting the size led to an error.
mGIFStruct.state = gif_error;
return;
}
Expand Down
3 changes: 1 addition & 2 deletions image/decoders/nsIconDecoder.cpp
Expand Up @@ -101,8 +101,7 @@ nsIconDecoder::WriteInternal(const char *aBuffer, PRUint32 aCount)
// Post our size to the superclass
PostSize(mWidth, mHeight);
if (HasError()) {
// Setting the size lead to an error; this can happen when for example
// a multipart channel sends an image of a different size.
// Setting the size led to an error.
mState = iconStateFinished;
return;
}
Expand Down
3 changes: 1 addition & 2 deletions image/decoders/nsJPEGDecoder.cpp
Expand Up @@ -263,8 +263,7 @@ nsJPEGDecoder::WriteInternal(const char *aBuffer, PRUint32 aCount)
// Post our size to the superclass
PostSize(mInfo.image_width, mInfo.image_height);
if (HasError()) {
// Setting the size lead to an error; this can happen when for example
// a multipart channel sends an image of a different size.
// Setting the size led to an error.
mState = JPEG_ERROR;
return;
}
Expand Down
3 changes: 1 addition & 2 deletions image/decoders/nsPNGDecoder.cpp
Expand Up @@ -519,8 +519,7 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
// Post our size to the superclass
decoder->PostSize(width, height);
if (decoder->HasError()) {
// Setting the size lead to an error; this can happen when for example
// a multipart channel sends an image of a different size.
// Setting the size led to an error.
longjmp(png_jmpbuf(decoder->mPNG), 1);
}

Expand Down
7 changes: 6 additions & 1 deletion image/public/imgIRequest.idl
Expand Up @@ -52,7 +52,7 @@ interface nsIPrincipal;
* @version 0.1
* @see imagelib2
*/
[scriptable, uuid(d35a9adb-8328-4b64-b06f-72a165acd080)]
[scriptable, uuid(a5a785a8-9881-11e1-aaff-001fbc092072)]
interface imgIRequest : nsIRequest
{
/**
Expand Down Expand Up @@ -132,6 +132,11 @@ interface imgIRequest : nsIRequest
*/
readonly attribute nsIPrincipal imagePrincipal;

/**
* Whether the request is multipart (ie, multipart/x-mixed-replace)
*/
readonly attribute bool multipart;

/**
* CORS modes images can be loaded with.
*
Expand Down
50 changes: 38 additions & 12 deletions image/src/RasterImage.cpp
Expand Up @@ -343,7 +343,7 @@ RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
// If we don't have a decoder, we know we've got everything we're going to
// get. If we do, we only display fully-downloaded frames; everything else
// gets delayed.
bool haveFullNextFrame = !mDecoder ||
bool haveFullNextFrame = (mMultipart && mBytesDecoded == 0) || !mDecoder ||
nextFrameIndex < mDecoder->GetCompleteFrameCount();

// If we're done decoding the next frame, go ahead and display it now and
Expand Down Expand Up @@ -1136,14 +1136,9 @@ RasterImage::SetSize(PRInt32 aWidth, PRInt32 aHeight)
return NS_ERROR_INVALID_ARG;

// if we already have a size, check the new size against the old one
if (mHasSize &&
if (!mMultipart && mHasSize &&
((aWidth != mSize.width) || (aHeight != mSize.height))) {

// Alter the warning depending on whether the channel is multipart
if (!mMultipart)
NS_WARNING("Image changed size on redecode! This should not happen!");
else
NS_WARNING("Multipart channel sent an image of a different size");
NS_WARNING("Image changed size on redecode! This should not happen!");

// Make the decoder aware of the error so that it doesn't try to call
// FinishInternal during ShutdownDecoder.
Expand Down Expand Up @@ -1215,10 +1210,14 @@ RasterImage::EnsureFrame(PRUint32 aFrameNum, PRInt32 aX, PRInt32 aY,
}
}

// Not reusable, so replace the frame directly.
DeleteImgFrame(aFrameNum);
return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
aPaletteDepth, imageData, imageLength,
paletteData, paletteLength);
mFrames.RemoveElementAt(aFrameNum);
nsAutoPtr<imgFrame> newFrame(new imgFrame());
nsresult rv = newFrame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
NS_ENSURE_SUCCESS(rv, rv);
return InternalAddFrameHelper(aFrameNum, newFrame.forget(), imageData,
imageLength, paletteData, paletteLength);
}

nsresult
Expand Down Expand Up @@ -1491,6 +1490,31 @@ RasterImage::AddSourceData(const char *aBuffer, PRUint32 aCount)
// This call should come straight from necko - no reentrancy allowed
NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");

// Starting a new part's frames, let's clean up before we add any
// This needs to happen just before we start getting EnsureFrame() call(s),
// so that there's no gap for anything to miss us.
if (mBytesDecoded == 0) {
// Our previous state may have been animated, so let's clean up
bool wasAnimating = mAnimating;
if (mAnimating) {
StopAnimation();
mAnimating = false;
}
mAnimationFinished = false;
if (mAnim) {
delete mAnim;
mAnim = nsnull;
}
// If there's only one frame, this could cause flickering
int old_frame_count = mFrames.Length();
if (old_frame_count > 1) {
for (int i = 0; i < old_frame_count; ++i) {
DeleteImgFrame(i);
}
mFrames.Clear();
}
}

// If we're not storing source data, write it directly to the decoder
if (!StoringSourceData()) {
rv = WriteToDecoder(aBuffer, aCount);
Expand Down Expand Up @@ -1620,7 +1644,7 @@ RasterImage::SourceDataComplete()
}

nsresult
RasterImage::NewSourceData()
RasterImage::NewSourceData(const char* aMimeType)
{
nsresult rv;

Expand Down Expand Up @@ -1655,6 +1679,8 @@ RasterImage::NewSourceData()
mDecoded = false;
mHasSourceData = false;

mSourceDataMimeType.Assign(aMimeType);

// We're decode-on-load here. Open up a new decoder just like what happens when
// we call Init() for decode-on-load images.
rv = InitDecoder(/* aDoSizeDecode = */ false);
Expand Down
2 changes: 1 addition & 1 deletion image/src/RasterImage.h
Expand Up @@ -304,7 +304,7 @@ class RasterImage : public Image
nsresult SourceDataComplete();

/* Called for multipart images when there's a new source image to add. */
nsresult NewSourceData();
nsresult NewSourceData(const char *aMimeType);

/**
* A hint of the number of bytes of source data that the image contains. If
Expand Down
19 changes: 12 additions & 7 deletions image/src/imgRequest.cpp
Expand Up @@ -768,14 +768,19 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt

// If we're multipart, and our image is initialized, fix things up for another round
if (mIsMultiPartChannel && mImage) {
if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
// Update the content type for this new part
nsCOMPtr<nsIChannel> partChan(do_QueryInterface(aRequest));
partChan->GetContentType(mContentType);
if (mContentType.EqualsLiteral(SVG_MIMETYPE) ||
mImage->GetType() == imgIContainer::TYPE_VECTOR) {
// mImage won't be reusable due to format change or a new SVG part
// Reset the tracker and forget that we have data for OnDataAvailable to
// treat its next call as a fresh image.
mStatusTracker = new imgStatusTracker(nsnull);
mGotData = false;
} else if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
// Inform the RasterImage that we have new source data
static_cast<RasterImage*>(mImage.get())->NewSourceData();
} else { // imageType == imgIContainer::TYPE_VECTOR
nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
NS_ABORT_IF_FALSE(imageAsStream,
"SVG-typed Image failed QI to nsIStreamListener");
imageAsStream->OnStartRequest(aRequest, ctxt);
static_cast<RasterImage*>(mImage.get())->NewSourceData(mContentType.get());
}
}

Expand Down
2 changes: 2 additions & 0 deletions image/src/imgRequest.h
Expand Up @@ -131,6 +131,8 @@ class imgRequest : public imgIDecoderObserver,
// wins.
static void SetCacheValidation(imgCacheEntry* aEntry, nsIRequest* aRequest);

bool GetMultipart() const { return mIsMultiPartChannel; }

// The CORS mode for which we loaded this image.
PRInt32 GetCORSMode() const { return mCORSMode; }

Expand Down
15 changes: 15 additions & 0 deletions image/src/imgRequestProxy.cpp
Expand Up @@ -558,6 +558,17 @@ NS_IMETHODIMP imgRequestProxy::GetImagePrincipal(nsIPrincipal **aPrincipal)
return NS_OK;
}

/* readonly attribute bool multipart; */
NS_IMETHODIMP imgRequestProxy::GetMultipart(bool *aMultipart)
{
if (!mOwner)
return NS_ERROR_FAILURE;

*aMultipart = mOwner->GetMultipart();

return NS_OK;
}

/* readonly attribute PRInt32 CORSMode; */
NS_IMETHODIMP imgRequestProxy::GetCORSMode(PRInt32* aCorsMode)
{
Expand Down Expand Up @@ -695,6 +706,10 @@ void imgRequestProxy::OnStopContainer(imgIContainer *image)
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStopContainer(this, image);
}

// Multipart needs reset for next OnStartContainer
if (mOwner && mOwner->GetMultipart())
mSentStartContainer = false;
}

void imgRequestProxy::OnStopDecode(nsresult status, const PRUnichar *statusArg)
Expand Down
2 changes: 1 addition & 1 deletion image/src/imgRequestProxy.h
Expand Up @@ -253,7 +253,7 @@ class imgRequestProxy : public imgIRequest,
bool mDeferNotifications;

// We only want to send OnStartContainer once for each proxy, but we might
// get multiple OnStartContainer calls (e.g. from multipart/x-mixed-replace).
// get multiple OnStartContainer calls.
bool mSentStartContainer;
};

Expand Down
5 changes: 4 additions & 1 deletion layout/generic/nsImageFrame.cpp
Expand Up @@ -652,7 +652,10 @@ nsImageFrame::OnStopDecode(imgIRequest *aRequest,
return NS_ERROR_FAILURE;
}

if (loadType == nsIImageLoadingContent::PENDING_REQUEST) {
bool multipart = false;
aRequest->GetMultipart(&multipart);

if (loadType == nsIImageLoadingContent::PENDING_REQUEST || multipart) {
NotifyNewCurrentRequest(aRequest, aStatus);
}

Expand Down

0 comments on commit 30d7a6f

Please sign in to comment.