--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -519,17 +519,19 @@ nsWindowsShellService::SetShouldCheckDef
return NS_OK;
}
static nsresult
WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
{
nsRefPtr<gfxImageSurface> image;
- nsresult rv = aImage->CopyCurrentFrame(getter_AddRefs(image));
+ nsresult rv = aImage->CopyFrame(imgIContainer::FRAME_FIRST,
+ imgIContainer::FLAG_SYNC_DECODE,
+ getter_AddRefs(image));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 width = image->Width();
PRInt32 height = image->Height();
PRUint8* bits = image->Data();
PRUint32 length = image->GetDataSize();
PRUint32 bpr = PRUint32(image->Stride());
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -263,16 +263,24 @@ nsImageLoadingContent::OnStopDecode(imgI
NS_IMETHODIMP
nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, PRBool aLastPart)
{
LOOP_OVER_OBSERVERS(OnStopRequest(aRequest, aLastPart));
return NS_OK;
}
+NS_IMETHODIMP
+nsImageLoadingContent::OnDiscard(imgIRequest *aRequest)
+{
+ LOOP_OVER_OBSERVERS(OnDiscard(aRequest));
+
+ return NS_OK;
+}
+
/*
* nsIImageLoadingContent impl
*/
NS_IMETHODIMP
nsImageLoadingContent::GetLoadingEnabled(PRBool *aLoadingEnabled)
{
*aLoadingEnabled = mLoadingEnabled;
--- a/content/base/src/nsStubImageDecoderObserver.cpp
+++ b/content/base/src/nsStubImageDecoderObserver.cpp
@@ -95,14 +95,20 @@ nsStubImageDecoderObserver::OnStopDecode
NS_IMETHODIMP
nsStubImageDecoderObserver::OnStopRequest(imgIRequest *aRequest,
PRBool aIsLastPart)
{
return NS_OK;
}
+NS_IMETHODIMP
+nsStubImageDecoderObserver::OnDiscard(imgIRequest *aRequest)
+{
+ return NS_OK;
+}
+
NS_IMETHODIMP
nsStubImageDecoderObserver::FrameChanged(imgIContainer *aContainer,
nsIntRect * aDirtyRect)
{
return NS_OK;
}
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -1324,18 +1324,21 @@ nsCanvasRenderingContext2D::CreatePatter
extend = gfxPattern::EXTEND_REPEAT;
} else if (repeat.EqualsLiteral("no-repeat")) {
extend = gfxPattern::EXTEND_NONE;
} else {
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
return NS_ERROR_DOM_SYNTAX_ERR;
}
+ // The canvas spec says that createPattern should use the first frame
+ // of animated images
nsLayoutUtils::SurfaceFromElementResult res =
- nsLayoutUtils::SurfaceFromElement(image, nsLayoutUtils::SFE_WANT_NEW_SURFACE);
+ nsLayoutUtils::SurfaceFromElement(image, nsLayoutUtils::SFE_WANT_FIRST_FRAME |
+ nsLayoutUtils::SFE_WANT_NEW_SURFACE);
if (!res.mSurface)
return NS_ERROR_NOT_AVAILABLE;
nsRefPtr<gfxPattern> thebespat = new gfxPattern(res.mSurface);
thebespat->SetExtend(extend);
nsRefPtr<nsCanvasPattern> pat = new nsCanvasPattern(thebespat, res.mPrincipal,
@@ -2960,26 +2963,30 @@ nsCanvasRenderingContext2D::DrawImage()
NS_GET_IID(nsIDOMElement),
ctx, argv[0]))
return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
gfxMatrix matrix;
nsRefPtr<gfxPattern> pattern;
nsRefPtr<gfxPath> path;
+ // The canvas spec says that drawImage should draw the first frame
+ // of animated images
+ PRUint32 sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
nsLayoutUtils::SurfaceFromElementResult res =
- nsLayoutUtils::SurfaceFromElement(imgElt);
+ nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags);
if (!res.mSurface)
return NS_ERROR_NOT_AVAILABLE;
#ifndef WINCE
// On non-CE, force a copy if we're using drawImage with our destination
// as a source to work around some Cairo self-copy semantics issues.
if (res.mSurface == mSurface) {
- res = nsLayoutUtils::SurfaceFromElement(imgElt, nsLayoutUtils::SFE_WANT_NEW_SURFACE);
+ sfeFlags |= nsLayoutUtils::SFE_WANT_NEW_SURFACE;
+ res = nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags);
if (!res.mSurface)
return NS_ERROR_NOT_AVAILABLE;
}
#endif
nsRefPtr<gfxASurface> imgsurf = res.mSurface;
nsCOMPtr<nsIPrincipal> principal = res.mPrincipal;
gfxIntSize imgSize = res.mSize;
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -2826,17 +2826,17 @@ var canvas108 = document.getElementById(
var ctx108 = canvas108.getContext('2d');
var isDone_test_2d_drawImage_animated_apng = false;
function test_2d_drawImage_animated_apng() {
deferTest();
setTimeout(wrapFunction(function () {
ctx108.drawImage(document.getElementById('anim-gr_1.png'), 0, 0);
- todo_isPixel(ctx108, 50,25, 0,255,0,255, 2);
+ isPixel(ctx108, 50,25, 0,255,0,255, 2);
isDone_test_2d_drawImage_animated_apng = true;
}), 5000);
}
</script>
<img src="image_anim-gr.png" id="anim-gr_1.png" class="resource">
@@ -2851,17 +2851,17 @@ var canvas109 = document.getElementById(
var ctx109 = canvas109.getContext('2d');
var isDone_test_2d_drawImage_animated_gif = false;
function test_2d_drawImage_animated_gif() {
deferTest();
setTimeout(wrapFunction(function () {
ctx109.drawImage(document.getElementById('anim-gr_1.gif'), 0, 0);
- todo_isPixel(ctx109, 50,25, 0,255,0,255, 2);
+ isPixel(ctx109, 50,25, 0,255,0,255, 2);
isDone_test_2d_drawImage_animated_gif = true;
}), 5000);
}
</script>
<img src="image_anim-gr.gif" id="anim-gr_1.gif" class="resource">
@@ -14485,18 +14485,18 @@ function test_2d_pattern_animated_gif()
deferTest();
setTimeout(function () {
var pattern = ctx458.createPattern(document.getElementById('anim-gr_2.gif'), 'repeat');
ctx458.fillStyle = pattern;
ctx458.fillRect(0, 0, 50, 50);
setTimeout(wrapFunction(function () {
ctx458.fillRect(50, 0, 50, 50);
- todo_isPixel(ctx458, 25,25, 0,255,0,255, 2);
- todo_isPixel(ctx458, 75,25, 0,255,0,255, 2);
+ isPixel(ctx458, 25,25, 0,255,0,255, 2);
+ isPixel(ctx458, 75,25, 0,255,0,255, 2);
isDone_test_2d_pattern_animated_gif = true;
}), 2500);
}, 2500);
}
</script>
<img src="image_anim-gr.gif" id="anim-gr_2.gif" class="resource">
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -5388,17 +5388,19 @@ nsSVGFEImageElement::Filter(nsSVGFilterI
getter_AddRefs(currentRequest));
nsCOMPtr<imgIContainer> imageContainer;
if (currentRequest)
currentRequest->GetImage(getter_AddRefs(imageContainer));
nsRefPtr<gfxASurface> currentFrame;
if (imageContainer)
- imageContainer->GetCurrentFrame(getter_AddRefs(currentFrame));
+ imageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_NONE,
+ getter_AddRefs(currentFrame));
// We need to wrap the surface in a pattern to have somewhere to set the
// graphics filter.
nsRefPtr<gfxPattern> thebesPattern;
if (currentFrame)
thebesPattern = new gfxPattern(currentFrame);
if (thebesPattern) {
@@ -5477,16 +5479,22 @@ nsSVGFEImageElement::FrameChanged(imgICo
}
NS_IMETHODIMP
nsSVGFEImageElement::OnStartContainer(imgIRequest *aRequest,
imgIContainer *aContainer)
{
nsresult rv =
nsImageLoadingContent::OnStartContainer(aRequest, aContainer);
+
+ // Request a decode
+ NS_ABORT_IF_FALSE(aContainer, "who sent the notification then?");
+ aContainer->RequestDecode();
+
+ // We have a size - invalidate
Invalidate();
return rv;
}
//----------------------------------------------------------------------
// helper methods
void
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -2099,17 +2099,17 @@ DrawBorderImage(nsPresContext* aPr
imgIRequest *req = aBorderStyle.GetBorderImage();
#ifdef DEBUG
{
PRUint32 status = imgIRequest::STATUS_ERROR;
if (req)
req->GetImageStatus(&status);
- NS_ASSERTION(req && (status & imgIRequest::STATUS_FRAME_COMPLETE),
+ NS_ASSERTION(req && (status & imgIRequest::STATUS_LOAD_COMPLETE),
"no image to draw");
}
#endif
// Get the actual image, and determine where the split points are.
// Note that mBorderImageSplit is in image pixels, not necessarily
// CSS pixels.
@@ -2295,31 +2295,33 @@ DrawBorderImageComponent(nsIRenderingCon
PRUint8 aHFill,
PRUint8 aVFill,
const nsSize& aUnitSize)
{
if (aFill.IsEmpty() || aSrc.IsEmpty())
return;
nsCOMPtr<imgIContainer> subImage;
- if (NS_FAILED(aImage->ExtractCurrentFrame(aSrc, getter_AddRefs(subImage))))
+ if (NS_FAILED(aImage->ExtractFrame(imgIContainer::FRAME_CURRENT, aSrc,
+ imgIContainer::FLAG_SYNC_DECODE,
+ getter_AddRefs(subImage))))
return;
gfxPattern::GraphicsFilter graphicsFilter =
nsLayoutUtils::GetGraphicsFilterForFrame(aForFrame);
// If we have no tiling in either direction, we can skip the intermediate
// scaling step.
if ((aHFill == NS_STYLE_BORDER_IMAGE_STRETCH &&
aVFill == NS_STYLE_BORDER_IMAGE_STRETCH) ||
(aUnitSize.width == aFill.width &&
aUnitSize.height == aFill.height)) {
nsLayoutUtils::DrawSingleImage(&aRenderingContext, subImage,
graphicsFilter,
- aFill, aDirtyRect);
+ aFill, aDirtyRect, imgIContainer::FLAG_NONE);
return;
}
// Compute the scale and position of the master copy of the image.
nsRect tile;
switch (aHFill) {
case NS_STYLE_BORDER_IMAGE_STRETCH:
tile.x = aFill.x;
@@ -2354,17 +2356,18 @@ DrawBorderImageComponent(nsIRenderingCon
tile.height = aFill.height/ceil(gfxFloat(aFill.height)/aUnitSize.height);
break;
default:
NS_NOTREACHED("unrecognized border-image fill style");
}
nsLayoutUtils::DrawImage(&aRenderingContext, subImage, graphicsFilter,
- tile, aFill, tile.TopLeft(), aDirtyRect);
+ tile, aFill, tile.TopLeft(), aDirtyRect,
+ imgIContainer::FLAG_NONE);
}
// Begin table border-collapsing section
// These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
// At some point, all functions should be unified to include the additional functionality that these provide
static nscoord
RoundIntToPixel(nscoord aValue,
@@ -3081,16 +3084,19 @@ ImageRenderer::ImageRenderer(nsIFrame* a
ImageRenderer::~ImageRenderer()
{
}
PRBool
ImageRenderer::PrepareImage()
{
if (mImage.IsEmpty() || !mImage.IsComplete()) {
+ // Make sure the image is actually decoding
+ mImage.RequestDecode();
+
// We can not prepare the image for rendering if it is not fully loaded.
return PR_FALSE;
}
switch (mType) {
case eStyleImageType_Image:
{
nsCOMPtr<imgIContainer> srcImage;
@@ -3110,18 +3116,20 @@ ImageRenderer::PrepareImage()
// The cropped image has zero size
return PR_FALSE;
}
if (isEntireImage) {
// The cropped image is identical to the source image
mImageContainer.swap(srcImage);
} else {
nsCOMPtr<imgIContainer> subImage;
- nsresult rv = srcImage->ExtractCurrentFrame(actualCropRect,
- getter_AddRefs(subImage));
+ nsresult rv = srcImage->ExtractFrame(imgIContainer::FRAME_CURRENT,
+ actualCropRect,
+ imgIContainer::FLAG_NONE,
+ getter_AddRefs(subImage));
if (NS_FAILED(rv)) {
NS_WARNING("The cropped image contains no pixels to draw; "
"maybe the crop rect is outside the image frame rect");
return PR_FALSE;
}
mImageContainer.swap(subImage);
}
}
@@ -3185,17 +3193,17 @@ ImageRenderer::Draw(nsPresContext*
if (aDest.IsEmpty() || aFill.IsEmpty())
return;
switch (mType) {
case eStyleImageType_Image:
nsLayoutUtils::DrawImage(&aRenderingContext, mImageContainer,
nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame),
- aDest, aFill, aAnchor, aDirty);
+ aDest, aFill, aAnchor, aDirty, imgIContainer::FLAG_NONE);
break;
case eStyleImageType_Gradient:
nsCSSRendering::PaintGradient(aPresContext, aRenderingContext,
mGradientData, aDirty, aDest, aFill, aRepeat);
break;
case eStyleImageType_Null:
default:
break;
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -68,17 +68,18 @@ nsDisplayListBuilder::nsDisplayListBuild
mSaveVisibleRegionOfMovingContent(nsnull),
mIgnoreScrollFrame(nsnull),
mCurrentTableItem(nsnull),
mBuildCaret(aBuildCaret),
mEventDelivery(aIsForEvents),
mIsAtRootOfPseudoStackingContext(PR_FALSE),
mPaintAllFrames(PR_FALSE),
mAccurateVisibleRegions(PR_FALSE),
- mInTransform(PR_FALSE) {
+ mInTransform(PR_FALSE),
+ mSyncDecodeImages(PR_FALSE) {
PL_InitArenaPool(&mPool, "displayListArena", 1024, sizeof(void*)-1);
nsPresContext* pc = aReferenceFrame->PresContext();
nsIPresShell *shell = pc->PresShell();
PRBool suppressed;
shell->IsPaintingSuppressed(&suppressed);
mIsBackgroundOnly = suppressed;
if (pc->IsRenderingOnlySelection()) {
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -286,16 +286,30 @@ public:
PRBool IsInTransform() { return mInTransform; }
/**
* Indicate whether or not we're directly or indirectly under and
* nsDisplayTransform or SVG foreignObject.
*/
void SetInTransform(PRBool aInTransform) { mInTransform = aInTransform; }
/**
+ * @return PR_TRUE if images have been set to decode synchronously.
+ */
+ PRBool ShouldSyncDecodeImages() { return mSyncDecodeImages; }
+
+ /**
+ * Indicates whether we should synchronously decode images. If true, we decode
+ * and draw whatever image data has been loaded. If false, we just draw
+ * whatever has already been decoded.
+ */
+ void SetSyncDecodeImages(PRBool aSyncDecodeImages) {
+ mSyncDecodeImages = aSyncDecodeImages;
+ }
+
+ /**
* Subtracts aRegion from *aVisibleRegion. We avoid letting
* aVisibleRegion become overcomplex by simplifying it if necessary ---
* unless mAccurateVisibleRegions is set, in which case we let it
* get arbitrarily complex.
*/
void SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
const nsRegion& aRegion);
@@ -391,16 +405,17 @@ private:
PRPackedBool mEventDelivery;
PRPackedBool mIsBackgroundOnly;
PRPackedBool mIsAtRootOfPseudoStackingContext;
PRPackedBool mPaintAllFrames;
PRPackedBool mAccurateVisibleRegions;
// True when we're building a display list that's directly or indirectly
// under an nsDisplayTransform
PRPackedBool mInTransform;
+ PRPackedBool mSyncDecodeImages;
};
class nsDisplayItem;
class nsDisplayList;
/**
* nsDisplayItems are put in singly-linked lists rooted in an nsDisplayList.
* nsDisplayItemLink holds the link. The lists are linked from lowest to
* highest in z-order.
--- a/layout/base/nsImageLoader.cpp
+++ b/layout/base/nsImageLoader.cpp
@@ -58,39 +58,39 @@
#include "nsStyleContext.h"
#include "nsGkAtoms.h"
// Paint forcing
#include "prenv.h"
NS_IMPL_ISUPPORTS2(nsImageLoader, imgIDecoderObserver, imgIContainerObserver)
-nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRBool aReflowOnLoad,
+nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
nsImageLoader *aNextLoader)
: mFrame(aFrame),
- mReflowOnLoad(aReflowOnLoad),
+ mActions(aActions),
mNextLoader(aNextLoader)
{
}
nsImageLoader::~nsImageLoader()
{
mFrame = nsnull;
if (mRequest) {
mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
}
}
/* static */ already_AddRefed<nsImageLoader>
nsImageLoader::Create(nsIFrame *aFrame, imgIRequest *aRequest,
- PRBool aReflowOnLoad, nsImageLoader *aNextLoader)
+ PRUint32 aActions, nsImageLoader *aNextLoader)
{
nsRefPtr<nsImageLoader> loader =
- new nsImageLoader(aFrame, aReflowOnLoad, aNextLoader);
+ new nsImageLoader(aFrame, aActions, aNextLoader);
loader->Load(aRequest);
return loader.forget();
}
void
nsImageLoader::Destroy()
@@ -134,90 +134,103 @@ nsImageLoader::Load(imgIRequest *aImage)
return rv;
}
NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest,
imgIContainer *aImage)
{
- if (aImage)
- {
- /* Get requested animation policy from the pres context:
- * normal = 0
- * one frame = 1
- * one loop = 2
- */
- aImage->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
- // Ensure the animation (if any) is started.
- aImage->StartAnimation();
- }
+ NS_ABORT_IF_FALSE(aImage, "Who's calling us then?");
+
+ /* Get requested animation policy from the pres context:
+ * normal = 0
+ * one frame = 1
+ * one loop = 2
+ */
+ aImage->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
+ // Ensure the animation (if any) is started.
+ aImage->StartAnimation();
+
return NS_OK;
}
NS_IMETHODIMP nsImageLoader::OnStopFrame(imgIRequest *aRequest,
PRUint32 aFrame)
{
if (!mFrame)
return NS_ERROR_FAILURE;
-
-#ifdef NS_DEBUG
-// Make sure the image request status's STATUS_FRAME_COMPLETE flag has been set to ensure
-// the image will be painted when invalidated
- if (aRequest) {
- PRUint32 status = imgIRequest::STATUS_ERROR;
- nsresult rv = aRequest->GetImageStatus(&status);
- if (NS_SUCCEEDED(rv)) {
- NS_ASSERTION((status & imgIRequest::STATUS_FRAME_COMPLETE), "imgIRequest::STATUS_FRAME_COMPLETE not set");
- }
- }
-#endif
if (!mRequest) {
// We're in the middle of a paint anyway
return NS_OK;
}
-
- // Draw the background image
- RedrawDirtyFrame(nsnull);
+
+ // Take requested actions
+ if (mActions & ACTION_REFLOW_ON_DECODE) {
+ DoReflow();
+ }
+ if (mActions & ACTION_REDRAW_ON_DECODE) {
+ DoRedraw(nsnull);
+ }
return NS_OK;
}
+NS_IMETHODIMP nsImageLoader::OnStopRequest(imgIRequest *aRequest,
+ PRBool aLastPart)
+{
+ if (!mFrame)
+ return NS_ERROR_FAILURE;
+
+ if (!mRequest) {
+ // We're in the middle of a paint anyway
+ return NS_OK;
+ }
+
+ // Take requested actions
+ if (mActions & ACTION_REFLOW_ON_LOAD) {
+ DoReflow();
+ }
+ if (mActions & ACTION_REDRAW_ON_LOAD) {
+ DoRedraw(nsnull);
+ }
+}
+
NS_IMETHODIMP nsImageLoader::FrameChanged(imgIContainer *aContainer,
nsIntRect *dirtyRect)
{
if (!mFrame)
return NS_ERROR_FAILURE;
if (!mRequest) {
// We're in the middle of a paint anyway
return NS_OK;
}
nsRect r = dirtyRect->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel());
- RedrawDirtyFrame(&r);
+ DoRedraw(&r);
return NS_OK;
}
+void
+nsImageLoader::DoReflow()
+{
+ nsIPresShell *shell = mFrame->PresContext()->GetPresShell();
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ shell->FrameNeedsReflow(mFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
+ NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not reflow after loading border-image");
+}
void
-nsImageLoader::RedrawDirtyFrame(const nsRect* aDamageRect)
+nsImageLoader::DoRedraw(const nsRect* aDamageRect)
{
- if (mReflowOnLoad) {
- nsIPresShell *shell = mFrame->PresContext()->GetPresShell();
-#ifdef DEBUG
- nsresult rv =
-#endif
- shell->FrameNeedsReflow(mFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
- NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not reflow after loading border-image");
- // The reflow might not do all the invalidation we need, so continue
- // on with the invalidation codepath.
- }
// NOTE: It is not sufficient to invalidate only the size of the image:
// the image may be tiled!
// The best option is to call into the frame, however lacking this
// we have to at least invalidate the frame's bounds, hence
// as long as we have a frame we'll use its size.
//
// Invalidate the entire frame
--- a/layout/base/nsImageLoader.h
+++ b/layout/base/nsImageLoader.h
@@ -52,45 +52,60 @@ class nsIURI;
* Image loaders pass notifications for background and border image
* loading and animation on to the frames.
*
* Each frame's image loaders form a linked list.
*/
class nsImageLoader : public nsStubImageDecoderObserver
{
private:
- nsImageLoader(nsIFrame *aFrame, PRBool aReflowOnLoad,
+ nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
nsImageLoader *aNextLoader);
virtual ~nsImageLoader();
public:
+ /*
+ * Flags to specify actions that can be taken for the image at various
+ * times. Reflows always occur before redraws. "Decode" refers to one
+ * frame being available, whereas "load" refers to all the data being loaded
+ * from the network.
+ */
+ enum {
+ ACTION_REFLOW_ON_DECODE = 0x01,
+ ACTION_REDRAW_ON_DECODE = 0x02,
+ ACTION_REFLOW_ON_LOAD = 0x04,
+ ACTION_REDRAW_ON_LOAD = 0x08
+ };
static already_AddRefed<nsImageLoader>
Create(nsIFrame *aFrame, imgIRequest *aRequest,
- PRBool aReflowOnLoad, nsImageLoader *aNextLoader);
+ PRUint32 aActions, nsImageLoader *aNextLoader);
NS_DECL_ISUPPORTS
// imgIDecoderObserver (override nsStubImageDecoderObserver)
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
NS_IMETHOD OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame);
+ NS_IMETHOD OnStopRequest(imgIRequest *aRequest, PRBool aLastPart);
// Do not override OnDataAvailable since background images are not
// displayed incrementally; they are displayed after the entire image
// has been loaded.
// Note: Images referenced by the <img> element are displayed
// incrementally in nsImageFrame.cpp.
// imgIContainerObserver (override nsStubImageDecoderObserver)
NS_IMETHOD FrameChanged(imgIContainer *aContainer, nsIntRect *dirtyRect);
void Destroy();
imgIRequest *GetRequest() { return mRequest; }
nsImageLoader *GetNextLoader() { return mNextLoader; }
private:
nsresult Load(imgIRequest *aImage);
- void RedrawDirtyFrame(const nsRect* aDamageRect);
+ void DoReflow();
+ /* if aDamageRect is nsnull, the whole frame is redrawn. */
+ void DoRedraw(const nsRect* aDamageRect);
nsIFrame *mFrame;
nsCOMPtr<imgIRequest> mRequest;
- PRBool mReflowOnLoad;
+ PRUint32 mActions;
nsRefPtr<nsImageLoader> mNextLoader;
};
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1056,16 +1056,19 @@ nsLayoutUtils::PaintFrame(nsIRenderingCo
nsAutoDisableGetUsedXAssertions disableAssert;
nsDisplayListBuilder builder(aFrame, PR_FALSE, PR_TRUE);
nsDisplayList list;
nsRect dirtyRect = aDirtyRegion.GetBounds();
if (aFlags & PAINT_IN_TRANSFORM) {
builder.SetInTransform(PR_TRUE);
}
+ if (aFlags & PAINT_SYNC_DECODE_IMAGES) {
+ builder.SetSyncDecodeImages(PR_TRUE);
+ }
nsresult rv;
builder.EnterPresShell(aFrame, dirtyRect);
rv = aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list);
if (NS_SUCCEEDED(rv) && aFrame->GetType() == nsGkAtoms::pageContentFrame) {
// We may need to paint out-of-flow frames whose placeholders are
@@ -2769,17 +2772,18 @@ MapToFloatUserPixels(const gfxSize& aSiz
static nsresult
DrawImageInternal(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
gfxPattern::GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
- const nsIntSize& aImageSize)
+ const nsIntSize& aImageSize,
+ PRUint32 aImageFlags)
{
if (aDest.IsEmpty() || aFill.IsEmpty())
return NS_OK;
nsCOMPtr<nsIDeviceContext> dc;
aRenderingContext->GetDeviceContext(*getter_AddRefs(dc));
gfxFloat appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
gfxContext *ctx = aRenderingContext->ThebesContext();
@@ -2867,25 +2871,27 @@ DrawImageInternal(nsIRenderingContext* a
// computed for edge pixels.
if (didSnap && !transform.HasNonIntegerTranslation()) {
dirty.RoundOut();
finalFillRect = fill.Intersect(dirty);
}
if (finalFillRect.IsEmpty())
return NS_OK;
- aImage->Draw(ctx, aGraphicsFilter, transform, finalFillRect, intSubimage);
+ aImage->Draw(ctx, aGraphicsFilter, transform, finalFillRect, intSubimage,
+ aImageFlags);
return NS_OK;
}
/* static */ nsresult
nsLayoutUtils::DrawSingleUnscaledImage(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
const nsPoint& aDest,
const nsRect& aDirty,
+ PRUint32 aImageFlags,
const nsRect* aSourceArea)
{
nsIntSize imageSize;
aImage->GetWidth(&imageSize.width);
aImage->GetHeight(&imageSize.height);
NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
nscoord appUnitsPerCSSPixel = nsIDeviceContext::AppUnitsPerCSSPixel();
@@ -2901,25 +2907,26 @@ nsLayoutUtils::DrawSingleUnscaledImage(n
nsRect dest(aDest - source.TopLeft(), size);
nsRect fill(aDest, source.Size());
// Ensure that only a single image tile is drawn. If aSourceArea extends
// outside the image bounds, we want to honor the aSourceArea-to-aDest
// translation but we don't want to actually tile the image.
fill.IntersectRect(fill, dest);
return DrawImageInternal(aRenderingContext, aImage, gfxPattern::FILTER_NEAREST,
- dest, fill, aDest, aDirty, imageSize);
+ dest, fill, aDest, aDirty, imageSize, aImageFlags);
}
/* static */ nsresult
nsLayoutUtils::DrawSingleImage(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
gfxPattern::GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aDirty,
+ PRUint32 aImageFlags,
const nsRect* aSourceArea)
{
nsIntSize imageSize;
aImage->GetWidth(&imageSize.width);
aImage->GetHeight(&imageSize.height);
NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
nsRect source;
@@ -2934,36 +2941,37 @@ nsLayoutUtils::DrawSingleImage(nsIRender
nsRect dest = nsLayoutUtils::GetWholeImageDestination(imageSize, source,
aDest);
// Ensure that only a single image tile is drawn. If aSourceArea extends
// outside the image bounds, we want to honor the aSourceArea-to-aDest
// transform but we don't want to actually tile the image.
nsRect fill;
fill.IntersectRect(aDest, dest);
return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, dest, fill,
- fill.TopLeft(), aDirty, imageSize);
+ fill.TopLeft(), aDirty, imageSize, aImageFlags);
}
/* static */ nsresult
nsLayoutUtils::DrawImage(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
gfxPattern::GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
- const nsRect& aDirty)
+ const nsRect& aDirty,
+ PRUint32 aImageFlags)
{
nsIntSize imageSize;
aImage->GetWidth(&imageSize.width);
aImage->GetHeight(&imageSize.height);
NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
aDest, aFill, aAnchor, aDirty,
- imageSize);
+ imageSize, aImageFlags);
}
/* static */ nsRect
nsLayoutUtils::GetWholeImageDestination(const nsIntSize& aWholeImageSize,
const nsRect& aImageSourceArea,
const nsRect& aDestArea)
{
double scaleX = double(aDestArea.width)/aImageSourceArea.width;
@@ -3359,18 +3367,23 @@ nsLayoutUtils::SurfaceFromElement(nsIDOM
return result;
}
nsCOMPtr<imgIContainer> imgContainer;
rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
if (NS_FAILED(rv) || !imgContainer)
return result;
+ PRUint32 whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME)
+ ? (PRUint32) imgIContainer::FRAME_FIRST
+ : (PRUint32) imgIContainer::FRAME_CURRENT;
nsRefPtr<gfxASurface> framesurf;
- rv = imgContainer->GetCurrentFrame(getter_AddRefs(framesurf));
+ rv = imgContainer->GetFrame(whichFrame,
+ imgIContainer::FLAG_SYNC_DECODE,
+ getter_AddRefs(framesurf));
if (NS_FAILED(rv))
return result;
PRInt32 imgWidth, imgHeight;
rv = imgContainer->GetWidth(&imgWidth);
rv |= imgContainer->GetHeight(&imgHeight);
if (NS_FAILED(rv))
return result;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -57,16 +57,17 @@ class nsIFontMetrics;
#include "nsAutoPtr.h"
#include "nsStyleSet.h"
#include "nsIView.h"
#include "nsIFrame.h"
#include "nsThreadUtils.h"
#include "nsIPresShell.h"
#include "nsIPrincipal.h"
#include "gfxPattern.h"
+#include "imgIContainer.h"
class nsBlockFrame;
class nsTextFragment;
/**
* nsLayoutUtils is a namespace class used for various helper
* functions that are useful in multiple places in layout. The goal
* is not to define multiple copies of the same static helper.
@@ -471,29 +472,34 @@ public:
*
* @param aRect The graphics rect to round outward.
* @param aFactor The number of app units per graphics unit.
* @return The smallest rectangle in app space that contains aRect.
*/
static nsRect RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor);
- enum { PAINT_IN_TRANSFORM = 0x01 };
+ enum {
+ PAINT_IN_TRANSFORM = 0x01,
+ PAINT_SYNC_DECODE_IMAGES = 0x02
+ };
+
/**
* Given aFrame, the root frame of a stacking context, paint it and its
* descendants to aRenderingContext.
* @param aRenderingContext a rendering context translated so that (0,0)
* is the origin of aFrame; for best results, (0,0) should transform
* to pixel-aligned coordinates
* @param aDirtyRegion the region that must be painted, in the coordinates
* of aFrame
* @param aBackstop paint the dirty area with this color before drawing
* the actual content; pass NS_RGBA(0,0,0,0) to draw no background
* @param aFlags if PAINT_IN_TRANSFORM is set, then we assume
- * this is inside a transform or SVG foreignObject.
+ * this is inside a transform or SVG foreignObject. If
+ * PAINT_SYNC_DECODE_IMAGES is set, we force synchronous decode on all images.
*/
static nsresult PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFrame,
const nsRegion& aDirtyRegion, nscolor aBackstop,
PRUint32 aFlags = 0);
/**
* @param aRootFrame the root frame of the tree to be displayed
* @param aMovingFrame a frame that has moved
@@ -875,64 +881,70 @@ public:
* app units.
* @param aImage The image.
* @param aDest Where one copy of the image should mapped to.
* @param aFill The area to be filled with copies of the
* image.
* @param aAnchor A point in aFill which we will ensure is
* pixel-aligned in the output.
* @param aDirty Pixels outside this area may be skipped.
+ * @param aImageFlags Image flags of the imgIContainer::FLAG_* variety
*/
static nsresult DrawImage(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
gfxPattern::GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
- const nsRect& aDirty);
+ const nsRect& aDirty,
+ PRUint32 aImageFlags);
/**
* Draw a whole image without scaling or tiling.
*
* @param aRenderingContext Where to draw the image, set up with an
* appropriate scale and transform for drawing in
* app units.
* @param aImage The image.
* @param aDest The top-left where the image should be drawn
* @param aDirty Pixels outside this area may be skipped.
+ * @param aImageFlags Image flags of the imgIContainer::FLAG_* variety
* @param aSourceArea If non-null, this area is extracted from
* the image and drawn at aDest. It's
* in appunits. For best results it should
* be aligned with image pixels.
*/
static nsresult DrawSingleUnscaledImage(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
const nsPoint& aDest,
const nsRect& aDirty,
+ PRUint32 aImageFlags,
const nsRect* aSourceArea = nsnull);
/**
* Draw a whole image without tiling.
*
* @param aRenderingContext Where to draw the image, set up with an
* appropriate scale and transform for drawing in
* app units.
* @param aImage The image.
* @param aDest The area that the image should fill
* @param aDirty Pixels outside this area may be skipped.
* @param aSourceArea If non-null, this area is extracted from
* the image and drawn in aDest. It's
* in appunits. For best results it should
* be aligned with image pixels.
+ * @param aImageFlags Image flags of the imgIContainer::FLAG_* variety
*/
static nsresult DrawSingleImage(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
gfxPattern::GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aDirty,
+ PRUint32 aImageFlags,
const nsRect* aSourceArea = nsnull);
/**
* Given a source area of an image (in appunits) and a destination area
* that we want to map that source area too, computes the area that
* would be covered by the whole image. This is useful for passing to
* the aDest parameter of DrawImage, when we want to draw a subimage
* of an overall image.
@@ -1064,17 +1076,20 @@ public:
* The above results are modified by the below flags (copying,
* forcing image surface, etc.).
*/
enum {
/* Always create a new surface for the result */
SFE_WANT_NEW_SURFACE = 1 << 0,
/* When creating a new surface, create an image surface */
- SFE_WANT_IMAGE_SURFACE = 1 << 1
+ SFE_WANT_IMAGE_SURFACE = 1 << 1,
+ /* Whether to extract the first frame (as opposed to the
+ current frame) in the case that the element is an image. */
+ SFE_WANT_FIRST_FRAME = 1 << 2
};
struct SurfaceFromElementResult {
/* mSurface will contain the resulting surface, or will be NULL on error */
nsRefPtr<gfxASurface> mSurface;
/* The size of the surface */
gfxIntSize mSize;
/* The principal associated with the element whose surface was returned */
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1271,30 +1271,34 @@ nsPresContext::SetImageLoaders(nsIFrame*
void
nsPresContext::SetupBackgroundImageLoaders(nsIFrame* aFrame,
const nsStyleBackground* aStyleBackground)
{
nsRefPtr<nsImageLoader> loaders;
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, aStyleBackground) {
if (aStyleBackground->mLayers[i].mImage.GetType() == eStyleImageType_Image) {
+ PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_DECODE;
imgIRequest *image = aStyleBackground->mLayers[i].mImage.GetImageData();
- loaders = nsImageLoader::Create(aFrame, image, PR_FALSE, loaders);
+ loaders = nsImageLoader::Create(aFrame, image, actions, loaders);
}
}
SetImageLoaders(aFrame, BACKGROUND_IMAGE, loaders);
}
void
nsPresContext::SetupBorderImageLoaders(nsIFrame* aFrame,
const nsStyleBorder* aStyleBorder)
{
+ PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_LOAD;
+ if (aStyleBorder->ImageBorderDiffers())
+ actions |= nsImageLoader::ACTION_REFLOW_ON_LOAD;
nsRefPtr<nsImageLoader> loader =
nsImageLoader::Create(aFrame, aStyleBorder->GetBorderImage(),
- aStyleBorder->ImageBorderDiffers(), nsnull);
+ actions, nsnull);
SetImageLoaders(aFrame, BORDER_IMAGE, loader);
}
void
nsPresContext::StopImagesFor(nsIFrame* aTargetFrame)
{
for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
SetImageLoaders(aTargetFrame, ImageLoadType(i), nsnull);
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5177,16 +5177,17 @@ PresShell::RenderDocument(const nsRect&
nsIFrame* rootScrollFrame = GetRootScrollFrame();
if ((aFlags & RENDER_IGNORE_VIEWPORT_SCROLLING) && rootScrollFrame) {
nsPoint pos = GetRootScrollFrameAsScrollable()->GetScrollPosition();
rect.MoveBy(-pos);
builder.SetIgnoreScrollFrame(rootScrollFrame);
}
builder.SetBackgroundOnly(PR_FALSE);
+ builder.SetSyncDecodeImages(PR_TRUE);
builder.EnterPresShell(rootFrame, rect);
// Add the canvas background color.
nsresult rv =
rootFrame->PresContext()->PresShell()->AddCanvasBackgroundColorItem(
builder, list, rootFrame);
if (NS_SUCCEEDED(rv)) {
--- a/layout/base/tests/test_bug445810.html
+++ b/layout/base/tests/test_bug445810.html
@@ -2,26 +2,34 @@
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=445810
-->
<head>
<title>Test for Bug 445810</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/modules/libpr0n/test/mochitest/imgutils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=445810">Mozilla Bug 445810</a>
<div><p id="display"></p></div>
+<div style="display: none;"><img id="currimg"></div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 445810 **/
+// Once the border image is loaded, it isn't necessarily decoded. We need to
+// force a decode, but only have a good way of doing that for image elements,
+// not border images. However, we can take advantage of the image cache (which
+// shares images of the same url) and assign the same url to both.
+var currImageElem = document.getElementById("currimg");
+
function new_image_url()
{
var width = 10;
var height = 10;
var canvas = document.createElement("canvas");
canvas.setAttribute("width", width);
canvas.setAttribute("height", height);
@@ -55,51 +63,75 @@ p.style.height = "5px";
is(divcs.width, "5px", "correct width without a border");
is(divcs.height, "5px", "correct height without a border");
p.style.border = "3px solid";
is(divcs.width, "11px", // 3 (border-left) + 5 (width) + 3 (border-right)
"correct width without a border image");
is(divcs.height, "11px", // 3 (border-top) + 5 (height) + 3 (border-bottom)
"correct height without a border image");
-p.style.MozBorderImage = "url( " + new_image_url() + ") 2 2 2 2 / 7px 2px";
+currImageElem.src = new_image_url();
+p.style.MozBorderImage = "url( " + currImageElem.src + ") 2 2 2 2 / 7px 2px";
is(divcs.width, "11px", "border image not loaded yet");
is(divcs.height, "11px", "border image not loaded yet");
+currImageElem.onload = step2;
-setTimeout(step2, 0);
function step2() {
+ // We got here through onload
+ ok(isImageLoaded("currimg"), "image loaded");
+
+ // Force a decode
+ forceDecode("currimg");
+ ok(isFrameDecoded("currimg"), "frame decoded");
+
is(divcs.width, "9px", "border image loading caused reflow");
is(divcs.height, "19px", "border image loading caused reflow");
p.style.border = "";
is(divcs.width, "9px", "border image still shows with border-style:none");
is(divcs.height, "19px", "border image still shows with border-style:none");
p.style.MozBorderImage = "";
is(divcs.width, "5px", "correct width without a border");
is(divcs.height, "5px", "correct height without a border");
- p.style.MozBorderImage = "url( " + new_image_url() + ") 2 2 2 2 / 7px 2px";
+ currImageElem.src = new_image_url();
+ p.style.MozBorderImage = "url( " + currImageElem.src + ") 2 2 2 2 / 7px 2px";
is(divcs.width, "5px", "border image not loaded yet");
is(divcs.height, "5px", "border image not loaded yet");
- setTimeout(step3, 0);
+ currImageElem.onload = step3;
}
function step3() {
+ // We got here through onload
+ ok(isImageLoaded("currimg"), "image loaded");
+
+ // Force a decode
+ forceDecode("currimg");
+ ok(isFrameDecoded("currimg"), "frame decoded");
+
is(divcs.width, "9px", "border image loading caused reflow");
is(divcs.height, "19px", "border image loading caused reflow");
- p.style.MozBorderImage = "url( " + new_image_url() + ") 2 2 2 2";
+ currImageElem.src = new_image_url();
+ p.style.MozBorderImage = "url( " + currImageElem.src + ") 2 2 2 2";
p.style.border = "3px none";
is(divcs.width, "5px", "border image not loaded yet");
is(divcs.height, "5px", "border image not loaded yet");
- setTimeout(step4, 0);
+ currImageElem.onload = step4;
}
function step4() {
+ // We got here through onload
+ ok(isImageLoaded("currimg"), "image loaded");
+
+ // Force a decode
+ forceDecode("currimg");
+ ok(isFrameDecoded("currimg"), "frame decoded");
+
is(divcs.width, "11px", "border image loading caused reflow");
is(divcs.height, "11px", "border image loading caused reflow");
SimpleTest.finish();
}
</script>
</pre>
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -236,17 +236,17 @@ nsBulletFrame::PaintBullet(nsIRenderingC
nsCOMPtr<imgIContainer> imageCon;
mImageRequest->GetImage(getter_AddRefs(imageCon));
if (imageCon) {
nsRect dest(mPadding.left, mPadding.top,
mRect.width - (mPadding.left + mPadding.right),
mRect.height - (mPadding.top + mPadding.bottom));
nsLayoutUtils::DrawSingleImage(&aRenderingContext,
imageCon, nsLayoutUtils::GetGraphicsFilterForFrame(this),
- dest + aPt, aDirtyRect);
+ dest + aPt, aDirtyRect, imgIContainer::FLAG_NONE);
return;
}
}
}
const nsStyleColor* myColor = GetStyleColor();
nsCOMPtr<nsIFontMetrics> fm;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -6090,17 +6090,17 @@ void nsFrame::FillCursorInformationFromS
aCursor.mHaveHotspot = PR_FALSE;
aCursor.mHotspotX = aCursor.mHotspotY = 0.0f;
for (nsCursorImage *item = ui->mCursorArray,
*item_end = ui->mCursorArray + ui->mCursorArrayLength;
item < item_end; ++item) {
PRUint32 status;
nsresult rv = item->mImage->GetImageStatus(&status);
- if (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_FRAME_COMPLETE)) {
+ if (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_LOAD_COMPLETE)) {
// This is the one we want
item->mImage->GetImage(getter_AddRefs(aCursor.mContainer));
aCursor.mHaveHotspot = item->mHaveHotspot;
aCursor.mHotspotX = item->mHotspotX;
aCursor.mHotspotY = item->mHotspotY;
break;
}
}
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1045,17 +1045,18 @@ nsImageFrame::DisplayAltFeedback(nsIRend
if (imageStatus & imgIRequest::STATUS_FRAME_COMPLETE) {
nsCOMPtr<imgIContainer> imgCon;
aRequest->GetImage(getter_AddRefs(imgCon));
NS_ABORT_IF_FALSE(imgCon, "Frame Complete, but no image container?");
nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
inner.XMost() - size : inner.x,
inner.y, size, size);
nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
- nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect);
+ nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
+ imgIContainer::FLAG_NONE);
iconUsed = PR_TRUE;
}
// if we could not draw the icon, flag that we're waiting for it and
// just draw some graffiti in the mean time
if (!iconUsed) {
nscolor oldColor;
nscoord iconXPos = (vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
@@ -1139,32 +1140,37 @@ public:
private:
nsCOMPtr<imgIContainer> mImage;
};
void
nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
static_cast<nsImageFrame*>(mFrame)->
- PaintImage(*aCtx, aBuilder->ToReferenceFrame(mFrame), aDirtyRect, mImage);
+ PaintImage(*aCtx, aBuilder->ToReferenceFrame(mFrame), aDirtyRect, mImage,
+ aBuilder->ShouldSyncDecodeImages()
+ ? (PRUint32) imgIContainer::FLAG_SYNC_DECODE
+ : (PRUint32) imgIContainer::FLAG_NONE);
}
void
nsImageFrame::PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
- const nsRect& aDirtyRect, imgIContainer* aImage)
+ const nsRect& aDirtyRect, imgIContainer* aImage,
+ PRUint32 aFlags)
{
// Render the image into our content area (the area inside
// the borders and padding)
NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
nsRect inner = GetInnerArea() + aPt;
nsRect dest(inner.TopLeft(), mComputedSize);
dest.y -= GetContinuationOffset();
nsLayoutUtils::DrawSingleImage(&aRenderingContext, aImage,
- nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect);
+ nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
+ aFlags);
nsPresContext* presContext = PresContext();
nsImageMap* map = GetImageMap(presContext);
if (nsnull != map) {
aRenderingContext.PushState();
aRenderingContext.SetColor(NS_RGB(0, 0, 0));
aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
aRenderingContext.Translate(inner.x, inner.y);
@@ -1844,16 +1850,22 @@ nsImageFrame::IconLoad::OnStopRequest(im
frame = iter.GetNext();
frame->Invalidate(frame->GetRect());
}
return NS_OK;
}
NS_IMETHODIMP
+nsImageFrame::IconLoad::OnDiscard(imgIRequest *aRequest)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsImageFrame::IconLoad::FrameChanged(imgIContainer *aContainer,
nsIntRect * aDirtyRect)
{
nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
nsImageFrame *frame;
while (iter.HasMore()) {
frame = iter.GetNext();
frame->Invalidate(frame->GetRect());
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -210,17 +210,18 @@ protected:
nsIRenderingContext& aContext);
void DisplayAltText(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
const nsString& aAltText,
const nsRect& aRect);
void PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
- const nsRect& aDirtyRect, imgIContainer* aImage);
+ const nsRect& aDirtyRect, imgIContainer* aImage,
+ PRUint32 aFlags);
protected:
friend class nsImageListener;
nsresult OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
nsresult OnDataAvailable(imgIRequest *aRequest, PRBool aCurrentFrame,
const nsIntRect *rect);
nsresult OnStopDecode(imgIRequest *aRequest,
nsresult aStatus,
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -576,17 +576,18 @@ nsPageFrame::PaintPageContent(nsIRenderi
}
aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect);
nsRect backgroundRect = nsRect(nsPoint(0, 0), pageContentFrame->GetSize());
nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this,
rect, backgroundRect, 0);
nsLayoutUtils::PaintFrame(&aRenderingContext, pageContentFrame,
- nsRegion(rect), NS_RGBA(0,0,0,0));
+ nsRegion(rect), NS_RGBA(0,0,0,0),
+ nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES);
aRenderingContext.PopState();
}
void
nsPageFrame::SetSharedPageData(nsSharedPageData* aPD)
{
mPD = aPD;
--- a/layout/generic/nsSimplePageSequence.cpp
+++ b/layout/generic/nsSimplePageSequence.cpp
@@ -634,17 +634,18 @@ nsSimplePageSequenceFrame::PrintNextPage
renderingContext->ThebesContext()->Rotate(M_PI/2);
}
#endif // XP_UNIX && !XP_MACOSX
nsRect drawingRect(nsPoint(0, 0),
mCurrentPageFrame->GetSize());
nsRegion drawingRegion(drawingRect);
nsLayoutUtils::PaintFrame(renderingContext, mCurrentPageFrame,
- drawingRegion, NS_RGBA(0,0,0,0));
+ drawingRegion, NS_RGBA(0,0,0,0),
+ nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES);
if (mSelectionHeight >= 0 && selectionY < mSelectionHeight) {
selectionY += height;
printedPageNum++;
pf->SetPageNumInfo(printedPageNum, mTotalPages);
conFrame->SetPosition(conFrame->GetPosition() + nsPoint(0, -height));
nsContainerFrame::PositionChildViews(conFrame);
@@ -702,17 +703,18 @@ nsSimplePageSequenceFrame::PaintPageSequ
// Loop over the pages and paint them.
nsIFrame* child = GetFirstChild(nsnull);
while (child) {
nsPoint pt = child->GetPosition();
// The rendering context has to be translated before each call to PaintFrame
aRenderingContext.PushState();
aRenderingContext.Translate(pt.x, pt.y);
nsLayoutUtils::PaintFrame(&aRenderingContext, child,
- nsRegion(rect - pt), NS_RGBA(0,0,0,0));
+ nsRegion(rect - pt), NS_RGBA(0,0,0,0),
+ nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES);
aRenderingContext.PopState();
child = child->GetNextSibling();
}
aRenderingContext.PopState();
}
NS_IMETHODIMP
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1467,16 +1467,24 @@ nsStyleImage::ComputeActualCropRect(nsIn
nsIntRect imageRect(nsIntPoint(0, 0), imageSize);
aActualCropRect.IntersectRect(imageRect, cropRect);
if (aIsEntireImage)
*aIsEntireImage = (aActualCropRect == imageRect);
return PR_TRUE;
}
+nsresult
+nsStyleImage::RequestDecode()
+{
+ if ((mType == eStyleImageType_Image) && mImage)
+ return mImage->RequestDecode();
+ return NS_OK;
+}
+
PRBool
nsStyleImage::IsOpaque() const
{
if (!IsComplete())
return PR_FALSE;
if (mType == eStyleImageType_Gradient) {
// We could check if every stop color of the gradient is non-transparent.
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -246,16 +246,20 @@ struct nsStyleImage {
* @param aIsEntireImage PR_TRUE iff |aActualCropRect| is identical to the
* source image bounds.
* @return PR_TRUE iff |aActualCropRect| holds a meaningful value.
*/
PRBool ComputeActualCropRect(nsIntRect& aActualCropRect,
PRBool* aIsEntireImage = nsnull) const;
/**
+ * Requests a decode on the image.
+ */
+ nsresult RequestDecode();
+ /**
* @return PR_TRUE if the item is definitely opaque --- i.e., paints every
* pixel within its bounds opaquely, and the bounds contains at least a pixel.
*/
PRBool IsOpaque() const;
/**
* @return PR_TRUE if this image is fully loaded, and its size is calculated;
* always returns PR_TRUE if |mType| is |eStyleImageType_Gradient|.
*/
@@ -780,16 +784,17 @@ struct nsStyleBorder {
mBorderStyle[aSide] &= ~BORDER_STYLE_MASK;
mBorderStyle[aSide] |= (aStyle & BORDER_STYLE_MASK);
mComputedBorder.side(aSide) =
(HasVisibleStyle(aSide) ? mBorder.side(aSide) : 0);
}
// Defined in nsStyleStructInlines.h
inline PRBool IsBorderImageLoaded() const;
+ inline nsresult RequestDecode();
void GetBorderColor(PRUint8 aSide, nscolor& aColor,
PRBool& aForeground) const
{
aForeground = PR_FALSE;
NS_ASSERTION(aSide <= NS_SIDE_LEFT, "bad side");
if ((mBorderStyle[aSide] & BORDER_COLOR_SPECIAL) == 0)
aColor = mBorderColor[aSide];
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -46,25 +46,34 @@
#include "nsStyleStruct.h"
#include "imgIRequest.h"
inline void
nsStyleBorder::SetBorderImage(imgIRequest* aImage)
{
mBorderImage = aImage;
+
+ /*
+ * Request a decode to jump start decoding, and lock it to make sure it
+ * stays decoded.
+ */
+ if (mBorderImage) {
+ mBorderImage->RequestDecode();
+ mBorderImage->LockImage();
+ }
}
inline imgIRequest*
nsStyleBorder::GetBorderImage() const
{
return mBorderImage;
}
inline PRBool nsStyleBorder::IsBorderImageLoaded() const
{
PRUint32 status;
return mBorderImage &&
NS_SUCCEEDED(mBorderImage->GetImageStatus(&status)) &&
- (status & imgIRequest::STATUS_FRAME_COMPLETE);
+ (status & imgIRequest::STATUS_LOAD_COMPLETE);
}
#endif /* !defined(nsStyleStructInlines_h_) */
--- a/layout/svg/base/src/nsSVGImageFrame.cpp
+++ b/layout/svg/base/src/nsSVGImageFrame.cpp
@@ -232,19 +232,25 @@ nsSVGImageFrame::PaintSVG(nsSVGRenderSta
if (imageLoader)
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
getter_AddRefs(currentRequest));
if (currentRequest)
currentRequest->GetImage(getter_AddRefs(mImageContainer));
}
+ // XXXbholley - I don't think huge images in SVGs are common enough to
+ // warrant worrying about the responsiveness impact of doing synchronous
+ // decodes. The extra code complexity of determinining when we want to
+ // force sync probably just isn't worth it, so always pass FLAG_SYNC_DECODE
nsRefPtr<gfxASurface> currentFrame;
if (mImageContainer)
- mImageContainer->GetCurrentFrame(getter_AddRefs(currentFrame));
+ mImageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE,
+ getter_AddRefs(currentFrame));
// We need to wrap the surface in a pattern to have somewhere to set the
// graphics filter.
nsRefPtr<gfxPattern> thebesPattern;
if (currentFrame)
thebesPattern = new gfxPattern(currentFrame);
if (thebesPattern) {
--- a/layout/xul/base/src/nsImageBoxFrame.cpp
+++ b/layout/xul/base/src/nsImageBoxFrame.cpp
@@ -328,17 +328,20 @@ public:
const nsRect& aDirtyRect);
NS_DISPLAY_DECL_NAME("XULImage")
};
void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx, const nsRect& aDirtyRect)
{
static_cast<nsImageBoxFrame*>(mFrame)->
- PaintImage(*aCtx, aDirtyRect, aBuilder->ToReferenceFrame(mFrame));
+ PaintImage(*aCtx, aDirtyRect, aBuilder->ToReferenceFrame(mFrame),
+ aBuilder->ShouldSyncDecodeImages()
+ ? (PRUint32) imgIContainer::FLAG_SYNC_DECODE
+ : (PRUint32) imgIContainer::FLAG_NONE);
}
NS_IMETHODIMP
nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
nsresult rv = nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
@@ -354,17 +357,18 @@ nsImageBoxFrame::BuildDisplayList(nsDisp
if (!IsVisibleForPainting(aBuilder))
return NS_OK;
return aLists.Content()->AppendNewToTop(new (aBuilder) nsDisplayXULImage(this));
}
void
nsImageBoxFrame::PaintImage(nsIRenderingContext& aRenderingContext,
- const nsRect& aDirtyRect, nsPoint aPt)
+ const nsRect& aDirtyRect, nsPoint aPt,
+ PRUint32 aFlags)
{
nsRect rect;
GetClientRect(rect);
rect += aPt;
if (!mImageRequest)
return;
@@ -376,17 +380,17 @@ nsImageBoxFrame::PaintImage(nsIRendering
nsCOMPtr<imgIContainer> imgCon;
mImageRequest->GetImage(getter_AddRefs(imgCon));
if (imgCon) {
PRBool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
nsLayoutUtils::GetGraphicsFilterForFrame(this),
- rect, dirty, hasSubRect ? &mSubRect : nsnull);
+ rect, dirty, aFlags, hasSubRect ? &mSubRect : nsnull);
}
}
//
// DidSetStyleContext
//
// When the style context changes, make sure that all of our image is up to date.
--- a/layout/xul/base/src/nsImageBoxFrame.h
+++ b/layout/xul/base/src/nsImageBoxFrame.h
@@ -122,17 +122,17 @@ public:
nsresult status,
const PRUnichar *statusArg);
NS_IMETHOD FrameChanged(imgIContainer *container, nsIntRect *dirtyRect);
virtual ~nsImageBoxFrame();
void PaintImage(nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
- nsPoint aPt);
+ nsPoint aPt, PRUint32 aFlags);
protected:
nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext);
virtual void GetImageSize();
private:
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
@@ -3394,17 +3394,17 @@ nsTreeBodyFrame::PaintTwisty(PRInt32
// Center the image. XXX Obey vertical-align style prop?
if (imageSize.height < twistyRect.height) {
pt.y += (twistyRect.height - imageSize.height)/2;
}
// Paint the image.
nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
- pt, aDirtyRect, &imageSize);
+ pt, aDirtyRect, imgIContainer::FLAG_NONE, &imageSize);
}
}
}
}
void
nsTreeBodyFrame::PaintImage(PRInt32 aRowIndex,
nsTreeColumn* aColumn,
@@ -3525,17 +3525,18 @@ nsTreeBodyFrame::PaintImage(PRInt32
image->GetWidth(&rawImageSize.width);
image->GetHeight(&rawImageSize.height);
nsRect wholeImageDest =
nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect,
nsRect(destRect.TopLeft(), imageDestSize));
nsLayoutUtils::DrawImage(&aRenderingContext, image,
nsLayoutUtils::GetGraphicsFilterForFrame(this),
- wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect);
+ wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect,
+ imgIContainer::FLAG_NONE);
}
// Update the aRemainingWidth and aCurrX values.
imageRect.Inflate(imageMargin);
aRemainingWidth -= imageRect.width;
if (!isRTL)
aCurrX += imageRect.width;
}
@@ -3701,17 +3702,17 @@ nsTreeBodyFrame::PaintCheckbox(PRInt32
}
if (imageSize.width < checkboxRect.width) {
pt.x += (checkboxRect.width - imageSize.width)/2;
}
// Paint the image.
nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
- pt, aDirtyRect, &imageSize);
+ pt, aDirtyRect, imgIContainer::FLAG_NONE, &imageSize);
}
}
void
nsTreeBodyFrame::PaintProgressMeter(PRInt32 aRowIndex,
nsTreeColumn* aColumn,
const nsRect& aProgressMeterRect,
nsPresContext* aPresContext,
@@ -3766,17 +3767,18 @@ nsTreeBodyFrame::PaintProgressMeter(PRIn
if (image) {
PRInt32 width, height;
image->GetWidth(&width);
image->GetHeight(&height);
nsSize size(width*nsIDeviceContext::AppUnitsPerCSSPixel(),
height*nsIDeviceContext::AppUnitsPerCSSPixel());
nsLayoutUtils::DrawImage(&aRenderingContext, image,
nsLayoutUtils::GetGraphicsFilterForFrame(this),
- nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), aDirtyRect);
+ nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
+ aDirtyRect, imgIContainer::FLAG_NONE);
} else {
aRenderingContext.FillRect(meterRect);
}
}
else if (state == nsITreeView::PROGRESS_UNDETERMINED) {
// Adjust the rect for its border and padding.
AdjustForBorderPadding(meterContext, meterRect);
@@ -3786,17 +3788,18 @@ nsTreeBodyFrame::PaintProgressMeter(PRIn
if (image) {
PRInt32 width, height;
image->GetWidth(&width);
image->GetHeight(&height);
nsSize size(width*nsIDeviceContext::AppUnitsPerCSSPixel(),
height*nsIDeviceContext::AppUnitsPerCSSPixel());
nsLayoutUtils::DrawImage(&aRenderingContext, image,
nsLayoutUtils::GetGraphicsFilterForFrame(this),
- nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), aDirtyRect);
+ nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
+ aDirtyRect, imgIContainer::FLAG_NONE);
}
}
}
void
nsTreeBodyFrame::PaintDropFeedback(const nsRect& aDropFeedbackRect,
nsPresContext* aPresContext,
--- a/modules/libpr0n/build/nsImageModule.cpp
+++ b/modules/libpr0n/build/nsImageModule.cpp
@@ -185,17 +185,17 @@ static NS_METHOD ImageUnregisterProc(nsI
static const nsModuleComponentInfo components[] =
{
{ "image cache",
NS_IMGLOADER_CID,
"@mozilla.org/image/cache;1",
imgLoaderConstructor, },
{ "image container",
NS_IMGCONTAINER_CID,
- "@mozilla.org/image/container;2",
+ "@mozilla.org/image/container;3",
imgContainerConstructor, },
{ "image loader",
NS_IMGLOADER_CID,
"@mozilla.org/image/loader;1",
imgLoaderConstructor,
ImageRegisterProc, /* register the decoder mime types here */
ImageUnregisterProc, },
{ "image request proxy",
@@ -206,72 +206,72 @@ static const nsModuleComponentInfo compo
NS_IMGTOOLS_CID,
"@mozilla.org/image/tools;1",
imgToolsConstructor, },
#ifdef IMG_BUILD_DECODER_gif
// gif
{ "GIF Decoder",
NS_GIFDECODER2_CID,
- "@mozilla.org/image/decoder;2?type=image/gif",
+ "@mozilla.org/image/decoder;3?type=image/gif",
nsGIFDecoder2Constructor, },
#endif
#ifdef IMG_BUILD_DECODER_jpeg
// jpeg
{ "JPEG decoder",
NS_JPEGDECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/jpeg",
+ "@mozilla.org/image/decoder;3?type=image/jpeg",
nsJPEGDecoderConstructor, },
{ "JPEG decoder",
NS_JPEGDECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/pjpeg",
+ "@mozilla.org/image/decoder;3?type=image/pjpeg",
nsJPEGDecoderConstructor, },
{ "JPEG decoder",
NS_JPEGDECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/jpg",
+ "@mozilla.org/image/decoder;3?type=image/jpg",
nsJPEGDecoderConstructor, },
#endif
#ifdef IMG_BUILD_ENCODER_jpeg
// jpeg (encoder)
{ "JPEG Encoder",
NS_JPEGENCODER_CID,
"@mozilla.org/image/encoder;2?type=image/jpeg",
nsJPEGEncoderConstructor, },
#endif
#ifdef IMG_BUILD_DECODER_bmp
// bmp
{ "ICO Decoder",
NS_ICODECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/x-icon",
+ "@mozilla.org/image/decoder;3?type=image/x-icon",
nsICODecoderConstructor, },
{ "ICO Decoder",
NS_ICODECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/vnd.microsoft.icon",
+ "@mozilla.org/image/decoder;3?type=image/vnd.microsoft.icon",
nsICODecoderConstructor, },
{ "BMP Decoder",
NS_BMPDECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/bmp",
+ "@mozilla.org/image/decoder;3?type=image/bmp",
nsBMPDecoderConstructor, },
{ "BMP Decoder",
NS_BMPDECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/x-ms-bmp",
+ "@mozilla.org/image/decoder;3?type=image/x-ms-bmp",
nsBMPDecoderConstructor, },
#endif
#ifdef IMG_BUILD_DECODER_png
// png
{ "PNG Decoder",
NS_PNGDECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/png",
+ "@mozilla.org/image/decoder;3?type=image/png",
nsPNGDecoderConstructor, },
{ "PNG Decoder",
NS_PNGDECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/x-png",
+ "@mozilla.org/image/decoder;3?type=image/x-png",
nsPNGDecoderConstructor, },
#endif
#ifdef IMG_BUILD_ENCODER_png
// png
{ "PNG Encoder",
NS_PNGENCODER_CID,
"@mozilla.org/image/encoder;2?type=image/png",
nsPNGEncoderConstructor, },
--- a/modules/libpr0n/decoders/bmp/nsBMPDecoder.cpp
+++ b/modules/libpr0n/decoders/bmp/nsBMPDecoder.cpp
@@ -16,16 +16,17 @@
*
* The Initial Developer of the Original Code is
* Christian Biesinger <cbiesinger@web.de>.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Neil Rashbrook <neil@parkwaycc.co.uk>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -45,18 +46,16 @@
#include "nsBMPDecoder.h"
#include "nsIInputStream.h"
#include "nsIComponentManager.h"
#include "imgIContainerObserver.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
-#include "imgILoad.h"
-
#include "prlog.h"
#ifdef PR_LOGGING
PRLogModuleInfo *gBMPLog = PR_NewLogModule("BMPDecoder");
#endif
// Convert from row (1..height) to absolute line (0..height-1)
#define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1))
@@ -68,86 +67,95 @@ nsBMPDecoder::nsBMPDecoder()
{
mColors = nsnull;
mRow = nsnull;
mCurPos = mPos = mNumColors = mRowBytes = 0;
mOldLine = mCurLine = 1; // Otherwise decoder will never start
mState = eRLEStateInitial;
mStateData = 0;
mLOH = WIN_HEADER_LENGTH;
+ mError = PR_FALSE;
}
nsBMPDecoder::~nsBMPDecoder()
{
- delete[] mColors;
- if (mRow)
- free(mRow);
+ delete[] mColors;
+ if (mRow)
+ free(mRow);
}
-NS_IMETHODIMP nsBMPDecoder::Init(imgILoad *aLoad)
+NS_IMETHODIMP nsBMPDecoder::Init(imgIContainer *aImage,
+ imgIDecoderObserver *aObserver,
+ PRUint32 aFlags)
{
- PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Init(%p)\n", aLoad));
- mObserver = do_QueryInterface(aLoad);
+ PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Init(%p)\n", aImage));
+ mImage = aImage;
+ mObserver = aObserver;
+ mFlags = aFlags;
- nsresult rv;
- mImage = do_CreateInstance("@mozilla.org/image/container;2", &rv);
- if (NS_FAILED(rv))
- return rv;
+ // Fire OnStartDecode at init time to support bug 512435
+ if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
+ mObserver->OnStartDecode(nsnull);
- return aLoad->SetImage(mImage);
+ return NS_OK;
}
-NS_IMETHODIMP nsBMPDecoder::Close()
+NS_IMETHODIMP nsBMPDecoder::Close(PRUint32 aFlags)
{
PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Close()\n"));
- mImage->DecodingComplete();
- if (mObserver) {
- mObserver->OnStopFrame(nsnull, 0);
- mObserver->OnStopContainer(nsnull, mImage);
- mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
- mObserver = nsnull;
+ // Send notifications if appropriate
+ if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
+ !mError && !(aFlags & CLOSE_FLAG_DONTNOTIFY)) {
+ if (mObserver)
+ mObserver->OnStopFrame(nsnull, 0);
+ mImage->DecodingComplete();
+ if (mObserver) {
+ mObserver->OnStopContainer(nsnull, mImage);
+ mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
+ }
}
- mImage = nsnull;
return NS_OK;
}
NS_IMETHODIMP nsBMPDecoder::Flush()
{
return NS_OK;
}
NS_METHOD nsBMPDecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure,
const char* aFromRawSegment, PRUint32 aToOffset,
PRUint32 aCount, PRUint32 *aWriteCount)
{
nsBMPDecoder *decoder = reinterpret_cast<nsBMPDecoder*>(aClosure);
+
+ // Always read everything
*aWriteCount = aCount;
-
+
nsresult rv = decoder->ProcessData(aFromRawSegment, aCount);
- if (NS_FAILED(rv)) {
- *aWriteCount = 0;
- }
-
+ // Necko doesn't propagate rvs. Set a flag before returning.
+ if (NS_FAILED(rv))
+ decoder->mError = PR_TRUE;
return rv;
}
-NS_IMETHODIMP nsBMPDecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount, PRUint32 *aRetval)
+NS_IMETHODIMP nsBMPDecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount)
{
- PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::WriteFrom(%p, %lu, %p)\n", aInStr, aCount, aRetval));
+ PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::WriteFrom(%p, %lu, %p)\n", aInStr, aCount));
- nsresult rv = aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval);
-
- if (aCount != *aRetval) {
- *aRetval = aCount;
- return NS_ERROR_FAILURE;
+ // Decode, watching for errors.
+ nsresult rv = NS_OK;
+ PRUint32 ignored;
+ if (!mError)
+ rv = aInStr->ReadSegments(ReadSegCb, this, aCount, &ignored);
+ if (mError || NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
}
-
- return rv;
+ return NS_OK;
}
// ----------------------------------------
// Actual Data Processing
// ----------------------------------------
static void calcBitmask(PRUint32 aMask, PRUint8& aBegin, PRUint8& aLength)
{
@@ -197,18 +205,16 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
if (toCopy > aCount)
toCopy = aCount;
memcpy(mRawBuf + mPos, aBuffer, toCopy);
mPos += toCopy;
aCount -= toCopy;
aBuffer += toCopy;
}
if (mPos == BFH_LENGTH) {
- rv = mObserver->OnStartDecode(nsnull);
- NS_ENSURE_SUCCESS(rv, rv);
ProcessFileHeader();
if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M')
return NS_ERROR_FAILURE;
if (mBFH.bihsize == OS2_BIH_LENGTH)
mLOH = OS2_HEADER_LENGTH;
}
if (mPos >= BFH_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */
PRUint32 toCopy = mLOH - mPos;
@@ -223,16 +229,40 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
ProcessInfoHeader();
PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP image is %lix%lix%lu. compression=%lu\n",
mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression));
// Verify we support this bit depth
if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 &&
mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32)
return NS_ERROR_UNEXPECTED;
+ // BMPs with negative width are invalid
+ // Reject extremely wide images to keep the math sane
+ const PRInt32 k64KWidth = 0x0000FFFF;
+ if (mBIH.width < 0 || mBIH.width > k64KWidth)
+ return NS_ERROR_FAILURE;
+
+ PRUint32 real_height = (mBIH.height > 0) ? mBIH.height : -mBIH.height;
+
+ // Set the size and notify
+ rv = mImage->SetSize(mBIH.width, real_height);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (mObserver) {
+ rv = mObserver->OnStartContainer(nsnull, mImage);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // We have the size. If we're doing a header-only decode, we got what
+ // we came for.
+ if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
+ return NS_OK;
+
+ // We're doing a real decode.
+ mOldLine = mCurLine = real_height;
+
if (mBIH.bpp <= 8) {
mNumColors = 1 << mBIH.bpp;
if (mBIH.colors && mBIH.colors < mNumColors)
mNumColors = mBIH.colors;
// Always allocate 256 even though mNumColors might be smaller
mColors = new colorTable[256];
if (!mColors)
@@ -242,28 +272,16 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
}
else if (mBIH.compression != BI_BITFIELDS && mBIH.bpp == 16) {
// Use default 5-5-5 format
mBitFields.red = 0x7C00;
mBitFields.green = 0x03E0;
mBitFields.blue = 0x001F;
CalcBitShift();
}
- // BMPs with negative width are invalid
- // Reject extremely wide images to keep the math sane
- const PRInt32 k64KWidth = 0x0000FFFF;
- if (mBIH.width < 0 || mBIH.width > k64KWidth)
- return NS_ERROR_FAILURE;
-
- PRUint32 real_height = (mBIH.height > 0) ? mBIH.height : -mBIH.height;
- rv = mImage->Init(mBIH.width, real_height, mObserver);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mObserver->OnStartContainer(nsnull, mImage);
- NS_ENSURE_SUCCESS(rv, rv);
- mOldLine = mCurLine = real_height;
PRUint32 imageLength;
if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
rv = mImage->AppendFrame(0, 0, mBIH.width, real_height, gfxASurface::ImageFormatARGB32,
(PRUint8**)&mImageData, &imageLength);
} else {
// mRow is not used for RLE encoded images
mRow = (PRUint8*)malloc((mBIH.width * mBIH.bpp)/8 + 4);
@@ -286,18 +304,20 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
|| ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) {
PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n"));
return NS_ERROR_FAILURE;
}
// Clear the image, as the RLE may jump over areas
memset(mImageData, 0, imageLength);
}
- mObserver->OnStartFrame(nsnull, 0);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (mObserver) {
+ mObserver->OnStartFrame(nsnull, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
}
PRUint8 bpc; // bytes per color
bpc = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4; // OS/2 Bitmaps have no padding byte
if (mColors && (mPos >= mLOH && (mPos < (mLOH + mNumColors * bpc)))) {
// We will receive (mNumColors * bpc) bytes of color data
PRUint32 colorBytes = mPos - mLOH; // Number of bytes already received
PRUint8 colorNum = colorBytes / bpc; // Color which is currently received
PRUint8 at = colorBytes % bpc;
@@ -594,17 +614,18 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
if (rows) {
nsIntRect r(0, mBIH.height < 0 ? -mBIH.height - mOldLine : mCurLine,
mBIH.width, rows);
// Tell the image that its data has been updated
rv = mImage->FrameUpdated(0, r);
NS_ENSURE_SUCCESS(rv, rv);
- mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
+ if (mObserver)
+ mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
mOldLine = mCurLine;
}
return NS_OK;
}
void nsBMPDecoder::ProcessFileHeader()
{
--- a/modules/libpr0n/decoders/bmp/nsBMPDecoder.h
+++ b/modules/libpr0n/decoders/bmp/nsBMPDecoder.h
@@ -15,16 +15,17 @@
* The Original Code is the Mozilla BMP Decoder.
*
* The Initial Developer of the Original Code is
* Christian Biesinger <cbiesinger@web.de>.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -164,16 +165,17 @@ private:
/** Calculates the red-, green- and blueshift in mBitFields using
* the bitmasks from mBitFields */
NS_METHOD CalcBitShift();
nsCOMPtr<imgIDecoderObserver> mObserver;
nsCOMPtr<imgIContainer> mImage;
+ PRUint32 mFlags;
PRUint32 mPos;
BMPFILEHEADER mBFH;
BMPINFOHEADER mBIH;
char mRawBuf[36];
PRUint32 mLOH; ///< Length of the header
@@ -187,16 +189,17 @@ private:
PRUint8 *mRow; ///< Holds one raw line of the image
PRUint32 mRowBytes; ///< How many bytes of the row were already received
PRInt32 mCurLine; ///< Index of the line of the image that's currently being decoded
PRInt32 mOldLine; ///< Previous index of the line
PRInt32 mCurPos; ///< Index in the current line of the image
ERLEState mState; ///< Maintains the current state of the RLE decoding
PRUint32 mStateData;///< Decoding information that is needed depending on mState
+ PRBool mError; ///< Did we hit an error?
/** Set mBFH from the raw data in mRawBuf, converting from little-endian
* data to native data as necessary */
void ProcessFileHeader();
/** Set mBIH from the raw data in mRawBuf, converting from little-endian
* data to native data as necessary */
void ProcessInfoHeader();
};
--- a/modules/libpr0n/decoders/bmp/nsICODecoder.cpp
+++ b/modules/libpr0n/decoders/bmp/nsICODecoder.cpp
@@ -17,16 +17,17 @@
* The Initial Developer of the Original Code is
* Netscape.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
* Christian Biesinger <cbiesinger@web.de>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -43,17 +44,16 @@
#include <stdlib.h>
#include "nsICODecoder.h"
#include "nsIInputStream.h"
#include "nsIComponentManager.h"
#include "imgIContainerObserver.h"
-#include "imgILoad.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIProperties.h"
#include "nsISupportsPrimitives.h"
#include "nsAutoPtr.h"
@@ -77,55 +77,65 @@ PRUint32 nsICODecoder::CalcAlphaRowSize(
nsICODecoder::nsICODecoder()
{
mPos = mNumColors = mRowBytes = mImageOffset = mCurrIcon = mNumIcons = 0;
mCurLine = 1; // Otherwise decoder will never start
mColors = nsnull;
mRow = nsnull;
mHaveAlphaData = mDecodingAndMask = PR_FALSE;
+ mError = PR_FALSE;
}
nsICODecoder::~nsICODecoder()
{
}
-NS_IMETHODIMP nsICODecoder::Init(imgILoad *aLoad)
-{
- mObserver = do_QueryInterface(aLoad);
-
- mImage = do_CreateInstance("@mozilla.org/image/container;2");
- if (!mImage)
- return NS_ERROR_OUT_OF_MEMORY;
+NS_IMETHODIMP nsICODecoder::Init(imgIContainer *aImage,
+ imgIDecoderObserver *aObserver,
+ PRUint32 aFlags)
+{
+ // Grab parameters
+ mImage = aImage;
+ mObserver = aObserver;
+ mFlags = aFlags;
- return aLoad->SetImage(mImage);
+ // Fire OnStartDecode at init time to support bug 512435
+ if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
+ mObserver->OnStartDecode(nsnull);
+
+ return NS_OK;
}
-NS_IMETHODIMP nsICODecoder::Close()
+NS_IMETHODIMP nsICODecoder::Close(PRUint32 aFlags)
{
- // Tell the image that it's data has been updated
- nsIntRect r(0, 0, mDirEntry.mWidth, mDirEntry.mHeight);
- nsresult rv = mImage->FrameUpdated(0, r);
-
- mImage->DecodingComplete();
+ nsresult rv = NS_OK;
+
+ // Send notifications if appropriate
+ if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
+ !mError && !(aFlags & CLOSE_FLAG_DONTNOTIFY)) {
+ // Tell the image that it's data has been updated
+ nsIntRect r(0, 0, mDirEntry.mWidth, mDirEntry.mHeight);
+ rv = mImage->FrameUpdated(0, r);
+
- if (mObserver) {
- mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
- mObserver->OnStopFrame(nsnull, 0);
- mObserver->OnStopContainer(nsnull, 0);
- mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
- mObserver = nsnull;
+ if (mObserver) {
+ mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
+ mObserver->OnStopFrame(nsnull, 0);
+ }
+ mImage->DecodingComplete();
+ if (mObserver) {
+ mObserver->OnStopContainer(nsnull, 0);
+ mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
+ }
}
- mImage = nsnull;
-
mPos = 0;
delete[] mColors;
- mColors = nsnull;
mCurLine = 0;
mRowBytes = 0;
mImageOffset = 0;
mCurrIcon = 0;
mNumIcons = 0;
if (mRow) {
@@ -142,23 +152,39 @@ NS_IMETHODIMP nsICODecoder::Flush()
return NS_OK;
}
NS_METHOD nsICODecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure,
const char* aFromRawSegment, PRUint32 aToOffset,
PRUint32 aCount, PRUint32 *aWriteCount) {
nsICODecoder *decoder = reinterpret_cast<nsICODecoder*>(aClosure);
+
+ // Always read everything
*aWriteCount = aCount;
- return decoder->ProcessData(aFromRawSegment, aCount);
+
+ // Process
+ nsresult rv = decoder->ProcessData(aFromRawSegment, aCount);
+
+ // rvs might not propagate correctly. Set a flag before returning.
+ if (NS_FAILED(rv))
+ decoder->mError = PR_TRUE;
+ return rv;
}
-NS_IMETHODIMP nsICODecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount, PRUint32 *aRetval)
+NS_IMETHODIMP nsICODecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount)
{
- return aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval);
+ // Decode, watching for errors
+ nsresult rv = NS_OK;
+ PRUint32 ignored;
+ if (!mError)
+ rv = aInStr->ReadSegments(ReadSegCb, this, aCount, &ignored);
+ if (mError || NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
}
nsresult nsICODecoder::ProcessData(const char* aBuffer, PRUint32 aCount) {
if (!aCount) // aCount=0 means EOF
return NS_OK;
while (aCount && (mPos < ICONCOUNTOFFSET)) { // Skip to the # of icons.
if (mPos == 2) { // if the third byte is 1: This is an icon, 2: a cursor
@@ -235,20 +261,27 @@ nsresult nsICODecoder::ProcessData(const
mPos += toCopy;
aCount -= toCopy;
aBuffer += toCopy;
}
nsresult rv;
if (mPos == mImageOffset + BITMAPINFOSIZE) {
- rv = mObserver->OnStartDecode(nsnull);
- NS_ENSURE_SUCCESS(rv, rv);
ProcessInfoHeader();
+ rv = mImage->SetSize(mDirEntry.mWidth, mDirEntry.mHeight);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (mObserver) {
+ rv = mObserver->OnStartContainer(nsnull, mImage);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
+ return NS_OK;
+
if (mBIH.bpp <= 8) {
switch (mBIH.bpp) {
case 1:
mNumColors = 2;
break;
case 4:
mNumColors = 16;
break;
@@ -259,53 +292,49 @@ nsresult nsICODecoder::ProcessData(const
return NS_ERROR_FAILURE;
}
mColors = new colorTable[mNumColors];
if (!mColors)
return NS_ERROR_OUT_OF_MEMORY;
}
- rv = mImage->Init(mDirEntry.mWidth, mDirEntry.mHeight, mObserver);
- NS_ENSURE_SUCCESS(rv, rv);
-
if (mIsCursor) {
nsCOMPtr<nsIProperties> props(do_QueryInterface(mImage));
if (props) {
nsCOMPtr<nsISupportsPRUint32> intwrapx = do_CreateInstance("@mozilla.org/supports-PRUint32;1");
nsCOMPtr<nsISupportsPRUint32> intwrapy = do_CreateInstance("@mozilla.org/supports-PRUint32;1");
if (intwrapx && intwrapy) {
intwrapx->SetData(mDirEntry.mXHotspot);
intwrapy->SetData(mDirEntry.mYHotspot);
props->Set("hotspotX", intwrapx);
props->Set("hotspotY", intwrapy);
}
}
}
- rv = mObserver->OnStartContainer(nsnull, mImage);
- NS_ENSURE_SUCCESS(rv, rv);
-
mCurLine = mDirEntry.mHeight;
mRow = (PRUint8*)malloc((mDirEntry.mWidth * mBIH.bpp)/8 + 4);
// +4 because the line is padded to a 4 bit boundary, but I don't want
// to make exact calculations here, that's unnecessary.
// Also, it compensates rounding error.
if (!mRow)
return NS_ERROR_OUT_OF_MEMORY;
PRUint32 imageLength;
rv = mImage->AppendFrame(0, 0, mDirEntry.mWidth, mDirEntry.mHeight,
gfxASurface::ImageFormatARGB32, (PRUint8**)&mImageData, &imageLength);
NS_ENSURE_SUCCESS(rv, rv);
- mObserver->OnStartFrame(nsnull, 0);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (mObserver) {
+ mObserver->OnStartFrame(nsnull, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
}
if (mColors && (mPos >= mImageOffset + BITMAPINFOSIZE) &&
(mPos < (mImageOffset + BITMAPINFOSIZE + mNumColors * 4))) {
// We will receive (mNumColors * 4) bytes of color data
PRUint32 colorBytes = mPos - (mImageOffset + 40); // Number of bytes already received
PRUint8 colorNum = colorBytes / 4; // Color which is currently received
PRUint8 at = colorBytes % 4;
--- a/modules/libpr0n/decoders/bmp/nsICODecoder.h
+++ b/modules/libpr0n/decoders/bmp/nsICODecoder.h
@@ -16,16 +16,17 @@
*
* The Initial Developer of the Original Code is
* Netscape.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -88,18 +89,19 @@ private:
void ProcessDirEntry(IconDirEntry& aTarget);
void ProcessInfoHeader();
nsresult SetImageData();
PRUint32 CalcAlphaRowSize();
private:
+ nsCOMPtr<imgIContainer> mImage;
nsCOMPtr<imgIDecoderObserver> mObserver;
- nsCOMPtr<imgIContainer> mImage;
+ PRUint32 mFlags;
PRUint32 mPos;
PRUint16 mNumIcons;
PRUint16 mCurrIcon;
PRUint32 mImageOffset;
char mDirEntryArray[16];
IconDirEntry mDirEntry;
@@ -114,12 +116,13 @@ private:
PRUint32 mRowBytes; // How many bytes of the row were already received
PRInt32 mCurLine;
PRUint32* mImageData;
PRPackedBool mHaveAlphaData;
PRPackedBool mIsCursor;
PRPackedBool mDecodingAndMask;
+ PRPackedBool mError;
};
#endif
--- a/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp
+++ b/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Saari <saari@netscape.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -76,18 +77,16 @@ mailing address.
#include "nsIInterfaceRequestorUtils.h"
#include "nsGIFDecoder2.h"
#include "nsIInputStream.h"
#include "nsIComponentManager.h"
#include "imgIContainerObserver.h"
-#include "imgILoad.h"
-
#include "gfxColor.h"
#include "gfxPlatform.h"
#include "qcms.h"
/*
* GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's'
*
* Note, the hold will never need to be bigger than 256 bytes to gather up in the hold,
@@ -117,87 +116,125 @@ nsGIFDecoder2::nsGIFDecoder2()
, mLastFlushedRow(-1)
, mImageData(nsnull)
, mOldColor(0)
, mCurrentFrame(-1)
, mCurrentPass(0)
, mLastFlushedPass(0)
, mGIFOpen(PR_FALSE)
, mSawTransparency(PR_FALSE)
+ , mError(PR_FALSE)
{
// Clear out the structure, excluding the arrays
memset(&mGIFStruct, 0, sizeof(mGIFStruct));
}
nsGIFDecoder2::~nsGIFDecoder2()
{
- Close();
}
//******************************************************************************
/** imgIDecoder methods **/
//******************************************************************************
//******************************************************************************
-/* void init (in imgILoad aLoad); */
-NS_IMETHODIMP nsGIFDecoder2::Init(imgILoad *aLoad)
+/* void init (in imgIContainer aImage,
+ in imgIDecoderObserver aObsever,
+ in unsigned long aFlags); */
+NS_IMETHODIMP nsGIFDecoder2::Init(imgIContainer *aImage,
+ imgIDecoderObserver *aObserver,
+ PRUint32 aFlags)
{
- mObserver = do_QueryInterface(aLoad);
+ // Store parameters
+ mImageContainer = aImage;
+ mObserver = aObserver;
+ mFlags = aFlags;
- mImageContainer = do_CreateInstance("@mozilla.org/image/container;2");
- aLoad->SetImage(mImageContainer);
-
+ // Fire OnStartDecode at init time to support bug 512435
+ if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
+ mObserver->OnStartDecode(nsnull);
+
// Start with the version (GIF89a|GIF87a)
mGIFStruct.state = gif_type;
mGIFStruct.bytes_to_consume = 6;
return NS_OK;
}
//******************************************************************************
/** nsIOutputStream methods **/
//******************************************************************************
//******************************************************************************
/* void close (); */
-NS_IMETHODIMP nsGIFDecoder2::Close()
+NS_IMETHODIMP nsGIFDecoder2::Close(PRUint32 aFlags)
{
- if (mCurrentFrame == mGIFStruct.images_decoded)
- EndImageFrame();
- EndGIF();
+ // Send notifications if appropriate
+ if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
+ !mError && !(aFlags & CLOSE_FLAG_DONTNOTIFY)) {
+ if (mCurrentFrame == mGIFStruct.images_decoded)
+ EndImageFrame();
+ EndGIF(/* aSuccess = */ PR_TRUE);
+ }
PR_FREEIF(mGIFStruct.local_colormap);
+ mImageContainer = nsnull;
+
return NS_OK;
}
//******************************************************************************
/* void flush (); */
NS_IMETHODIMP nsGIFDecoder2::Flush()
{
return NS_OK;
}
//******************************************************************************
/* static callback from nsIInputStream::ReadSegments */
-static NS_METHOD ReadDataOut(nsIInputStream* in,
- void* closure,
- const char* fromRawSegment,
- PRUint32 toOffset,
- PRUint32 count,
- PRUint32 *writeCount)
+NS_METHOD nsGIFDecoder2::ReadDataOut(nsIInputStream* in,
+ void* closure,
+ const char* fromRawSegment,
+ PRUint32 toOffset,
+ PRUint32 count,
+ PRUint32 *writeCount)
{
nsGIFDecoder2 *decoder = static_cast<nsGIFDecoder2*>(closure);
- nsresult rv = decoder->ProcessData((unsigned char*)fromRawSegment, count, writeCount);
+
+ // Always read everything
+ *writeCount = count;
+
+ // Process
+ nsresult rv = decoder->ProcessData((unsigned char*)fromRawSegment, count);
+
+ // We do some fine-grained error control here. If we have at least one frame
+ // of an animated gif, we still want to display it (mostly for legacy reasons).
+ // libpr0n code is strict, so we have to lie and tell it we were successful. So
+ // if we have something to salvage, we send off final decode notifications, and
+ // pretend that we're decoded. Otherwise, we set mError.
if (NS_FAILED(rv)) {
- *writeCount = 0;
- return rv;
+
+ // Determine if we want to salvage the situation
+ PRUint32 numFrames = 0;
+ if (decoder->mImageContainer)
+ decoder->mImageContainer->GetNumFrames(&numFrames);
+
+ // If we're salvaging, send off notifications
+ if (numFrames > 1) { // XXXbholley - this is from the old code, but why not > 0?
+ decoder->EndGIF(/* aSuccess = */ PR_TRUE);
+ }
+
+ // Otherwise, set mError
+ else
+ decoder->mError = PR_TRUE;
}
+ // Necko is dubious with callbacks, so we don't use rvs to propagate errors.
return NS_OK;
}
// Push any new rows according to mCurrentPass/mLastFlushedPass and
// mCurrentRow/mLastFlushedRow. Note: caller is responsible for
// updating mlastFlushed{Row,Pass}.
nsresult
nsGIFDecoder2::FlushImageData(PRUint32 fromRow, PRUint32 rows)
@@ -239,90 +276,86 @@ nsGIFDecoder2::FlushImageData()
default: // more than one pass on - push the whole frame
rv = FlushImageData(0, mGIFStruct.height);
}
return rv;
}
//******************************************************************************
-nsresult nsGIFDecoder2::ProcessData(unsigned char *data, PRUint32 count, PRUint32 *_retval)
+nsresult nsGIFDecoder2::ProcessData(unsigned char *data, PRUint32 count)
{
// Push the data to the GIF decoder
-
nsresult rv = GifWrite(data, count);
NS_ENSURE_SUCCESS(rv, rv);
// Flushing is only needed for first frame
if (!mGIFStruct.images_decoded) {
rv = FlushImageData();
+ NS_ENSURE_SUCCESS(rv, rv);
mLastFlushedRow = mCurrentRow;
mLastFlushedPass = mCurrentPass;
}
- *_retval = count;
-
- return rv;
+ return NS_OK;
}
//******************************************************************************
-/* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
-NS_IMETHODIMP nsGIFDecoder2::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
+/* void writeFrom (in nsIInputStream inStr, in unsigned long count); */
+NS_IMETHODIMP nsGIFDecoder2::WriteFrom(nsIInputStream *inStr, PRUint32 count)
{
- nsresult rv = inStr->ReadSegments(ReadDataOut, this, count, _retval);
-
- /* necko doesn't propagate the errors from ReadDataOut - take matters
- into our own hands. if we have at least one frame of an animated
- gif, then return success so we keep displaying as much as possible. */
- if (mGIFStruct.state == gif_error || mGIFStruct.state == gif_oom) {
- PRUint32 numFrames = 0;
- if (mImageContainer)
- mImageContainer->GetNumFrames(&numFrames);
- if (numFrames <= 1)
- rv = NS_ERROR_FAILURE;
- }
-
- return rv;
+ // Decode, watching for errors
+ nsresult rv = NS_OK;
+ PRUint32 ignored;
+ if (!mError)
+ rv = inStr->ReadSegments(nsGIFDecoder2::ReadDataOut, this,
+ count, &ignored);
+ if (mError || NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
}
//******************************************************************************
// GIF decoder callback methods. Part of public API for GIF2
//******************************************************************************
//******************************************************************************
void nsGIFDecoder2::BeginGIF()
{
if (mGIFOpen)
return;
-
- if (mObserver)
- mObserver->OnStartDecode(nsnull);
- mImageContainer->Init(mGIFStruct.screen_width, mGIFStruct.screen_height, mObserver);
+ mGIFOpen = PR_TRUE;
+ mImageContainer->SetSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
if (mObserver)
mObserver->OnStartContainer(nsnull, mImageContainer);
- mGIFOpen = PR_TRUE;
+ // If we're doing a header-only decode, we have what we came for
+ if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
+ return;
}
//******************************************************************************
-void nsGIFDecoder2::EndGIF()
+void nsGIFDecoder2::EndGIF(PRBool aSuccess)
{
if (!mGIFOpen)
return;
+ if (aSuccess)
+ mImageContainer->DecodingComplete();
+
if (mObserver) {
mObserver->OnStopContainer(nsnull, mImageContainer);
- mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
+ mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
+ nsnull);
}
-
+
mImageContainer->SetLoopCount(mGIFStruct.loop_count);
- mImageContainer->DecodingComplete();
mGIFOpen = PR_FALSE;
}
//******************************************************************************
nsresult nsGIFDecoder2::BeginImageFrame(gfx_depth aDepth)
{
if (!mGIFStruct.images_decoded) {
@@ -330,17 +363,20 @@ nsresult nsGIFDecoder2::BeginImageFrame(
// if it has a y-axis offset. Otherwise, the area may never be refreshed
// and the placeholder will remain on the screen. (Bug 37589)
if (mGIFStruct.y_offset > 0) {
PRInt32 imgWidth;
mImageContainer->GetWidth(&imgWidth);
PRUint32 imgCurFrame;
mImageContainer->GetCurrentFrameIndex(&imgCurFrame);
nsIntRect r(0, 0, imgWidth, mGIFStruct.y_offset);
- mObserver->OnDataAvailable(nsnull, imgCurFrame == PRUint32(mGIFStruct.images_decoded), &r);
+ if (mObserver)
+ mObserver->OnDataAvailable(nsnull,
+ imgCurFrame == PRUint32(mGIFStruct.images_decoded),
+ &r);
}
}
PRUint32 imageDataLength;
nsresult rv;
gfxASurface::gfxImageFormat format;
if (mGIFStruct.is_transparent)
format = gfxASurface::ImageFormatARGB32;
@@ -386,20 +422,23 @@ void nsGIFDecoder2::EndImageFrame()
// If the first frame is smaller in height than the entire image, send a
// OnDataAvailable (Display Refresh) for the area it does not have data for.
// This will clear the remaining bits of the placeholder. (Bug 37589)
const PRUint32 realFrameHeight = mGIFStruct.height + mGIFStruct.y_offset;
if (realFrameHeight < mGIFStruct.screen_height) {
PRUint32 imgCurFrame;
mImageContainer->GetCurrentFrameIndex(&imgCurFrame);
- nsIntRect r(0, realFrameHeight,
- mGIFStruct.screen_width,
- mGIFStruct.screen_height - realFrameHeight);
- mObserver->OnDataAvailable(nsnull, imgCurFrame == PRUint32(mGIFStruct.images_decoded), &r);
+ nsIntRect r(0, realFrameHeight,
+ mGIFStruct.screen_width,
+ mGIFStruct.screen_height - realFrameHeight);
+ if (mObserver)
+ mObserver->OnDataAvailable(nsnull,
+ imgCurFrame == PRUint32(mGIFStruct.images_decoded),
+ &r);
}
// This transparency check is only valid for first frame
if (mGIFStruct.is_transparent && !mSawTransparency) {
mImageContainer->SetFrameHasNoAlpha(mGIFStruct.images_decoded);
}
}
mCurrentRow = mLastFlushedRow = -1;
mCurrentPass = mLastFlushedPass = 0;
@@ -1046,16 +1085,20 @@ nsresult nsGIFDecoder2::GifWrite(const P
(mGIFStruct.version == 87)) {
mGIFStruct.screen_height = mGIFStruct.height;
mGIFStruct.screen_width = mGIFStruct.width;
mGIFStruct.x_offset = 0;
mGIFStruct.y_offset = 0;
}
// Create the image container with the right size.
BeginGIF();
+
+ // If we were doing header-only, we're done
+ if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
+ return NS_OK;
}
/* Work around more broken GIF files that have zero image
width or height */
if (!mGIFStruct.height || !mGIFStruct.width) {
mGIFStruct.height = mGIFStruct.screen_height;
mGIFStruct.width = mGIFStruct.screen_width;
if (!mGIFStruct.height || !mGIFStruct.width) {
@@ -1177,38 +1220,38 @@ nsresult nsGIFDecoder2::GifWrite(const P
} else {
/* See if there are any more images in this sequence. */
EndImageFrame();
GETN(1, gif_image_start);
}
break;
case gif_done:
- EndGIF();
+ EndGIF(/* aSuccess = */ PR_TRUE);
return NS_OK;
break;
case gif_error:
- EndGIF();
+ EndGIF(/* aSuccess = */ PR_FALSE);
return NS_ERROR_FAILURE;
break;
// Handle out of memory errors
case gif_oom:
return NS_ERROR_OUT_OF_MEMORY;
// We shouldn't ever get here.
default:
break;
}
}
// if an error state is set but no data remains, code flow reaches here
if (mGIFStruct.state == gif_error) {
- EndGIF();
+ EndGIF(/* aSuccess = */ PR_FALSE);
return NS_ERROR_FAILURE;
}
// Copy the leftover into mGIFStruct.hold
mGIFStruct.bytes_in_hold = len;
if (len) {
// Add what we have sofar to the block
PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap :
--- a/modules/libpr0n/decoders/gif/nsGIFDecoder2.h
+++ b/modules/libpr0n/decoders/gif/nsGIFDecoder2.h
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Saari <saari@netscape.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -62,37 +63,44 @@ class nsGIFDecoder2 : public imgIDecoder
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IMGIDECODER
nsGIFDecoder2();
~nsGIFDecoder2();
- nsresult ProcessData(unsigned char *data, PRUint32 count, PRUint32 *_retval);
+ nsresult ProcessData(unsigned char *data, PRUint32 count);
+ static NS_METHOD ReadDataOut(nsIInputStream* in,
+ void* closure,
+ const char* fromRawSegment,
+ PRUint32 toOffset,
+ PRUint32 count,
+ PRUint32 *writeCount);
private:
/* These functions will be called when the decoder has a decoded row,
* frame size information, etc. */
void BeginGIF();
- void EndGIF();
+ void EndGIF(PRBool aSuccess);
nsresult BeginImageFrame(gfx_depth aDepth);
void EndImageFrame();
nsresult FlushImageData();
nsresult FlushImageData(PRUint32 fromRow, PRUint32 rows);
nsresult GifWrite(const PRUint8 * buf, PRUint32 numbytes);
PRUint32 OutputRow();
PRBool DoLzw(const PRUint8 *q);
inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
nsCOMPtr<imgIContainer> mImageContainer;
- nsCOMPtr<imgIDecoderObserver> mObserver; // this is just qi'd from mRequest for speed
+ nsCOMPtr<imgIDecoderObserver> mObserver;
+ PRUint32 mFlags;
PRInt32 mCurrentRow;
PRInt32 mLastFlushedRow;
PRUint8 *mImageData; // Pointer to image data in either Cairo or 8bit format
PRUint32 *mColormap; // Current colormap to be used in Cairo format
PRUint32 mColormapSize;
PRUint32 mOldColor; // The old value of the transparent pixel
@@ -100,13 +108,14 @@ private:
// of decoding it, and -1 otherwise.
PRInt32 mCurrentFrame;
PRUint8 mCurrentPass;
PRUint8 mLastFlushedPass;
PRUint8 mColorMask; // Apply this to the pixel to keep within colormap
PRPackedBool mGIFOpen;
PRPackedBool mSawTransparency;
+ PRPackedBool mError;
gif_struct mGIFStruct;
};
#endif
--- a/modules/libpr0n/decoders/icon/nsIconDecoder.cpp
+++ b/modules/libpr0n/decoders/icon/nsIconDecoder.cpp
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Scott MacGregor <mscott@netscape.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -36,112 +37,232 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsIconDecoder.h"
#include "nsIInputStream.h"
#include "imgIContainer.h"
#include "imgIContainerObserver.h"
-#include "imgILoad.h"
#include "nspr.h"
#include "nsIComponentManager.h"
#include "nsRect.h"
#include "nsComponentManagerUtils.h"
#include "nsIInterfaceRequestorUtils.h"
+#include "ImageErrors.h"
NS_IMPL_THREADSAFE_ADDREF(nsIconDecoder)
NS_IMPL_THREADSAFE_RELEASE(nsIconDecoder)
NS_INTERFACE_MAP_BEGIN(nsIconDecoder)
NS_INTERFACE_MAP_ENTRY(imgIDecoder)
NS_INTERFACE_MAP_END_THREADSAFE
-nsIconDecoder::nsIconDecoder()
+
+nsIconDecoder::nsIconDecoder() :
+ mImage(nsnull),
+ mObserver(nsnull),
+ mFlags(imgIDecoder::DECODER_FLAG_NONE),
+ mWidth(-1),
+ mHeight(-1),
+ mPixBytesRead(0),
+ mPixBytesTotal(0),
+ mImageData(nsnull),
+ mState(iconStateStart),
+ mNotifiedDone(PR_FALSE)
{
+ // Nothing to do
}
nsIconDecoder::~nsIconDecoder()
{ }
/** imgIDecoder methods **/
-NS_IMETHODIMP nsIconDecoder::Init(imgILoad *aLoad)
+NS_IMETHODIMP nsIconDecoder::Init(imgIContainer *aImage,
+ imgIDecoderObserver *aObserver,
+ PRUint32 aFlags)
{
- mObserver = do_QueryInterface(aLoad); // we're holding 2 strong refs to the request.
- mImage = do_CreateInstance("@mozilla.org/image/container;2");
- if (!mImage) return NS_ERROR_OUT_OF_MEMORY;
+ // Grab parameters
+ mImage = aImage;
+ mObserver = aObserver;
+ mFlags = aFlags;
- aLoad->SetImage(mImage);
+ // Fire OnStartDecode at init time to support bug 512435
+ if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
+ mObserver->OnStartDecode(nsnull);
return NS_OK;
}
-NS_IMETHODIMP nsIconDecoder::Close()
+NS_IMETHODIMP nsIconDecoder::Close(PRUint32 aFlags)
{
- mImage->DecodingComplete();
+ // If we haven't notified of completion yet for a full/success decode, we
+ // didn't finish. Notify in error mode
+ if (!(aFlags & CLOSE_FLAG_DONTNOTIFY) &&
+ !(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
+ !mNotifiedDone)
+ NotifyDone(/* aSuccess = */ PR_FALSE);
- if (mObserver)
- {
- mObserver->OnStopFrame(nsnull, 0);
- mObserver->OnStopContainer(nsnull, mImage);
- mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
- }
-
+ mImage = nsnull;
return NS_OK;
}
NS_IMETHODIMP nsIconDecoder::Flush()
{
return NS_OK;
}
-NS_IMETHODIMP nsIconDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
+static nsresult
+WriteIconData(nsIInputStream *aInStream, void *aClosure, const char *aFromSegment,
+ PRUint32 aToOffset, PRUint32 aCount, PRUint32 *aWriteCount)
{
- // read the header from the input stram...
- PRUint32 readLen;
- PRUint8 header[2];
- nsresult rv = inStr->Read((char*)header, 2, &readLen);
- NS_ENSURE_TRUE(readLen == 2, NS_ERROR_UNEXPECTED); // w, h
- count -= 2;
+ nsresult rv;
+
+ // We always read everything
+ *aWriteCount = aCount;
+
+ // We put this here to avoid errors about crossing initialization with case
+ // jumps on linux.
+ PRUint32 bytesToRead = 0;
+
+ // Grab the parameters
+ nsIconDecoder *decoder = static_cast<nsIconDecoder*>(aClosure);
+
+ // Performance isn't critical here, so our update rectangle is
+ // always the full icon
+ nsIntRect r(0, 0, decoder->mWidth, decoder->mHeight);
+
+ // Loop until the input data is gone
+ while (aCount > 0) {
+ switch (decoder->mState) {
+ case iconStateStart:
- PRInt32 w = header[0];
- PRInt32 h = header[1];
- NS_ENSURE_TRUE(w > 0 && h > 0, NS_ERROR_UNEXPECTED);
+ // Grab the width
+ decoder->mWidth = (PRUint8)*aFromSegment;
+
+ // Book Keeping
+ aFromSegment++;
+ aCount--;
+ decoder->mState = iconStateHaveHeight;
+ break;
+
+ case iconStateHaveHeight:
- if (mObserver)
- mObserver->OnStartDecode(nsnull);
- mImage->Init(w, h, mObserver);
- if (mObserver)
- mObserver->OnStartContainer(nsnull, mImage);
+ // Grab the Height
+ decoder->mHeight = (PRUint8)*aFromSegment;
- PRUint32 imageLen;
- PRUint8 *imageData;
+ // Set up the container and signal
+ decoder->mImage->SetSize(decoder->mWidth,
+ decoder->mHeight);
+ if (decoder->mObserver)
+ decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
+
+ // If We're doing a header-only decode, we're done
+ if (decoder->mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) {
+ decoder->mState = iconStateFinished;
+ break;
+ }
- rv = mImage->AppendFrame(0, 0, w, h, gfxASurface::ImageFormatARGB32, &imageData, &imageLen);
- if (NS_FAILED(rv))
- return rv;
+ // Add the frame and signal
+ rv = decoder->mImage->AppendFrame(0, 0,
+ decoder->mWidth,
+ decoder->mHeight,
+ gfxASurface::ImageFormatARGB32,
+ &decoder->mImageData,
+ &decoder->mPixBytesTotal);
+ if (NS_FAILED(rv)) {
+ decoder->mState = iconStateError;
+ return rv;
+ }
+ if (decoder->mObserver)
+ decoder->mObserver->OnStartFrame(nsnull, 0);
- if (mObserver)
- mObserver->OnStartFrame(nsnull, 0);
+ // Book Keeping
+ aFromSegment++;
+ aCount--;
+ decoder->mState = iconStateReadPixels;
+ break;
+
+ case iconStateReadPixels:
- // Ensure that there enough in the inputStream
- NS_ENSURE_TRUE(count >= imageLen, NS_ERROR_UNEXPECTED);
+ // How many bytes are we reading?
+ bytesToRead = PR_MAX(aCount,
+ decoder->mPixBytesTotal - decoder->mPixBytesRead);
+
+ // Copy the bytes
+ memcpy(decoder->mImageData + decoder->mPixBytesRead,
+ aFromSegment, bytesToRead);
- // Read the image data direct into the frame data
- rv = inStr->Read((char*)imageData, imageLen, &readLen);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(readLen == imageLen, NS_ERROR_UNEXPECTED);
+ // Notify
+ rv = decoder->mImage->FrameUpdated(0, r);
+ if (NS_FAILED(rv)) {
+ decoder->mState = iconStateError;
+ return rv;
+ }
+ if (decoder->mObserver)
+ decoder->mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
+
+ // Book Keeping
+ aFromSegment += bytesToRead;
+ aCount -= bytesToRead;
+ decoder->mPixBytesRead += bytesToRead;
- // Notify the image...
- nsIntRect r(0, 0, w, h);
- rv = mImage->FrameUpdated(0, r);
- if (NS_FAILED(rv))
- return rv;
+ // If we've got all the pixel bytes, we're finished
+ if (decoder->mPixBytesRead == decoder->mPixBytesTotal) {
+ decoder->NotifyDone(/* aSuccess = */ PR_TRUE);
+ decoder->mState = iconStateFinished;
+ }
+ break;
+
+ case iconStateFinished:
- mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
+ // Consume all excess data silently
+ aCount = 0;
+
+ break;
+
+ case iconStateError:
+ return NS_IMAGELIB_ERROR_FAILURE;
+ break;
+ }
+ }
return NS_OK;
}
+void
+nsIconDecoder::NotifyDone(PRBool aSuccess)
+{
+ // We should only call this once
+ NS_ABORT_IF_FALSE(!mNotifiedDone, "Calling NotifyDone twice");
+
+ // Notify
+ if (mObserver)
+ mObserver->OnStopFrame(nsnull, 0);
+ if (aSuccess)
+ mImage->DecodingComplete();
+ if (mObserver) {
+ mObserver->OnStopContainer(nsnull, mImage);
+ mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
+ nsnull);
+ }
+
+ // Flag that we've notified
+ mNotifiedDone = PR_TRUE;
+}
+
+
+NS_IMETHODIMP nsIconDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count)
+{
+ // Decode, watching for errors.
+ nsresult rv = NS_OK;
+ PRUint32 ignored;
+ if (mState != iconStateError)
+ rv = inStr->ReadSegments(WriteIconData, this, count, &ignored);
+ if ((mState == iconStateError) || NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
--- a/modules/libpr0n/decoders/icon/nsIconDecoder.h
+++ b/modules/libpr0n/decoders/icon/nsIconDecoder.h
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Scott MacGregor <mscott@netscape.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -79,14 +80,32 @@ class nsIconDecoder : public imgIDecoder
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IMGIDECODER
nsIconDecoder();
virtual ~nsIconDecoder();
-private:
nsCOMPtr<imgIContainer> mImage;
- nsCOMPtr<imgIDecoderObserver> mObserver; // this is just qi'd from mRequest for speed
+ nsCOMPtr<imgIDecoderObserver> mObserver;
+ PRUint32 mFlags;
+ PRUint8 mWidth;
+ PRUint8 mHeight;
+ PRUint32 mPixBytesRead;
+ PRUint32 mPixBytesTotal;
+ PRUint8* mImageData;
+ PRUint32 mState;
+
+ PRBool mNotifiedDone;
+ void NotifyDone(PRBool aSuccess);
};
+enum {
+ iconStateStart = 0,
+ iconStateHaveHeight = 1,
+ iconStateReadPixels = 2,
+ iconStateFinished = 3,
+ iconStateError = 4
+};
+
+
#endif // nsIconDecoder_h__
--- a/modules/libpr0n/decoders/icon/nsIconModule.cpp
+++ b/modules/libpr0n/decoders/icon/nsIconModule.cpp
@@ -92,17 +92,17 @@ static NS_METHOD IconDecoderUnregisterPr
#endif
static const nsModuleComponentInfo components[] =
{
#ifdef USE_ICON_DECODER
{ "icon decoder",
NS_ICONDECODER_CID,
- "@mozilla.org/image/decoder;2?type=image/icon",
+ "@mozilla.org/image/decoder;3?type=image/icon",
nsIconDecoderConstructor,
IconDecoderRegisterProc,
IconDecoderUnregisterProc, },
#endif
{ "Icon Protocol Handler",
NS_ICONPROTOCOL_CID,
NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-icon",
--- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp
+++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp
@@ -18,16 +18,17 @@
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Federico Mena-Quintero <federico@novell.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -94,17 +95,18 @@ static void cmyk_convert_rgb(JSAMPROW ro
/* Normal JFIF markers can't have more bytes than this. */
#define MAX_JPEG_MARKER_LENGTH (((PRUint32)1 << 16) - 1)
nsJPEGDecoder::nsJPEGDecoder()
{
mState = JPEG_HEADER;
mReading = PR_TRUE;
- mError = NS_OK;
+ mNotifiedDone = PR_FALSE;
+ mError = PR_FALSE;
mImageData = nsnull;
mBytesToSkip = 0;
memset(&mInfo, 0, sizeof(jpeg_decompress_struct));
memset(&mSourceMgr, 0, sizeof(mSourceMgr));
mInfo.client_data = (void*)this;
mSegment = nsnull;
@@ -132,21 +134,32 @@ nsJPEGDecoder::~nsJPEGDecoder()
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p",
this));
}
/** imgIDecoder methods **/
-/* void init (in imgILoad aLoad); */
-NS_IMETHODIMP nsJPEGDecoder::Init(imgILoad *aLoad)
+/* void init (in imgIContainer aImage,
+ in imgIDecoderObserver aObserver,
+ in unsigned long aFlags); */
+NS_IMETHODIMP nsJPEGDecoder::Init(imgIContainer *aImage,
+ imgIDecoderObserver *aObserver,
+ PRUint32 aFlags)
{
- mImageLoad = aLoad;
- mObserver = do_QueryInterface(aLoad);
+
+ /* Grab the parameters. */
+ mImage = aImage;
+ mObserver = aObserver;
+ mFlags = aFlags;
+
+ /* Fire OnStartDecode at init time to support bug 512435 */
+ if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
+ mObserver->OnStartDecode(nsnull);
/* We set up the normal JPEG error routines, then override error_exit. */
mInfo.err = jpeg_std_error(&mErr.pub);
/* mInfo.err = jpeg_std_error(&mErr.pub); */
mErr.pub.error_exit = my_error_exit;
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp(mErr.setjmp_buffer)) {
/* If we get here, the JPEG code has signaled an error.
@@ -168,139 +181,102 @@ NS_IMETHODIMP nsJPEGDecoder::Init(imgILo
mSourceMgr.skip_input_data = skip_input_data;
mSourceMgr.resync_to_restart = jpeg_resync_to_restart;
mSourceMgr.term_source = term_source;
/* Record app markers for ICC data */
for (PRUint32 m = 0; m < 16; m++)
jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
-
-
- /* Check if the request already has an image container.
- * this is the case when multipart/x-mixed-replace is being downloaded
- * if we already have one and it has the same width and height, reuse it.
- * This is also the case when an existing container is reloading itself from
- * us.
- *
- * If we have a mismatch in width/height for the container later on we will
- * generate an error.
- */
- mImageLoad->GetImage(getter_AddRefs(mImage));
-
- if (!mImage) {
- mImage = do_CreateInstance("@mozilla.org/image/container;2");
- if (!mImage)
- return NS_ERROR_OUT_OF_MEMORY;
-
- mImageLoad->SetImage(mImage);
- nsresult result = mImage->SetDiscardable("image/jpeg");
- if (NS_FAILED(result)) {
- mState = JPEG_ERROR;
- PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
- (" (could not set image container to discardable)"));
- return result;
- }
- }
-
return NS_OK;
}
/* void close (); */
-NS_IMETHODIMP nsJPEGDecoder::Close()
+NS_IMETHODIMP nsJPEGDecoder::Close(PRUint32 aFlags)
{
PR_LOG(gJPEGlog, PR_LOG_DEBUG,
("[this=%p] nsJPEGDecoder::Close\n", this));
/* Step 8: Release JPEG decompression object */
mInfo.src = nsnull;
jpeg_destroy_decompress(&mInfo);
- if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) {
- NS_WARNING("Never finished decoding the JPEG.");
- /* Tell imgLoader that image decoding has failed */
- return NS_ERROR_FAILURE;
- }
+ /* If we already know we're in an error state, don't
+ bother flagging another one here. */
+ if (mError)
+ return NS_OK;
+ /* If we're doing a full decode and haven't notified of completion yet,
+ * we must not have got everything we wanted. Send error notifications. */
+ if (!(aFlags & CLOSE_FLAG_DONTNOTIFY) &&
+ !(mFlags && imgIDecoder::DECODER_FLAG_HEADERONLY) &&
+ !mNotifiedDone)
+ NotifyDone(/* aSuccess = */ PR_FALSE);
+
+ /* Otherwise, no problems. */
return NS_OK;
}
/* void flush (); */
NS_IMETHODIMP nsJPEGDecoder::Flush()
{
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Flush");
- PRUint32 ret;
if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER && mState != JPEG_ERROR)
- return this->ProcessData(nsnull, 0, &ret);
+ return this->ProcessData(nsnull, 0);
return NS_OK;
}
static NS_METHOD ReadDataOut(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount)
{
nsJPEGDecoder *decoder = static_cast<nsJPEGDecoder*>(closure);
- nsresult rv = decoder->ProcessData(fromRawSegment, count, writeCount);
- if (NS_FAILED(rv)) {
- /* Tell imgLoader that image decoding has failed */
+
+ // We always read everything
+ *writeCount = count;
+
+ // Process some data
+ nsresult rv = decoder->ProcessData(fromRawSegment, count);
+
+ // Necko's error propagation is dubious - use an explicit flag
+ if (NS_FAILED(rv))
decoder->mError = rv;
- *writeCount = 0;
- }
-
- return NS_OK;
+ return rv;
}
-/* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
-NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *writeCount)
+/* void writeFrom (in nsIInputStream inStr, in unsigned long count); */
+NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count)
{
NS_ENSURE_ARG_POINTER(inStr);
- NS_ENSURE_ARG_POINTER(writeCount);
- /* necko doesn't propagate the errors from ReadDataOut */
- nsresult rv = inStr->ReadSegments(ReadDataOut, this, count, writeCount);
- if (NS_FAILED(mError)) {
- /* Tell imgLoader that image decoding has failed */
- rv = NS_ERROR_FAILURE;
- }
-
- return rv;
+ // Decode, watching for errors
+ nsresult rv = NS_OK;
+ PRUint32 ignored;
+ if (!mError)
+ rv = inStr->ReadSegments(ReadDataOut, this, count, &ignored);
+ if (mError || NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
}
//******************************************************************************
-nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 *writeCount)
+nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count)
{
LOG_SCOPE_WITH_PARAM(gJPEGlog, "nsJPEGDecoder::ProcessData", "count", count);
mSegment = (const JOCTET *)data;
mSegmentLen = count;
- *writeCount = count;
-
- if (data && count) {
- nsresult result = mImage->AddRestoreData((char *) data, count);
-
- if (NS_FAILED(result)) {
- mState = JPEG_ERROR;
- PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
- ("} (could not add restore data)"));
- return result;
- }
-
- PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
- (" added %u bytes to restore data",
- count));
- }
- // else no input stream.. Flush() ?
/* Return here if there is a fatal error. */
nsresult error_code;
if ((error_code = setjmp(mErr.setjmp_buffer)) != 0) {
mState = JPEG_SINK_NON_JPEG_TRAILER;
if (error_code == NS_ERROR_FAILURE) {
/* Error due to corrupt stream - return NS_OK so that libpr0n
doesn't throw away a partial image load */
@@ -327,16 +303,26 @@ nsresult nsJPEGDecoder::ProcessData(cons
/* Step 3: read file parameters with jpeg_read_header() */
if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) {
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
("} (JPEG_SUSPENDED)"));
return NS_OK; /* I/O suspension */
}
+ /* Set Width and height, and notify that the container is ready to go. */
+ mImage->SetSize(mInfo.image_width, mInfo.image_height);
+ if (mObserver)
+ mObserver->OnStartContainer(nsnull, mImage);
+
+ /* If we're doing a header-only decode, we're done. */
+ if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
+ return NS_OK;
+
+ /* We're doing a full decode. */
JOCTET *profile;
PRUint32 profileLength;
eCMSMode cmsMode = gfxPlatform::GetCMSMode();
if ((cmsMode != eCMSMode_Off) &&
read_icc_profile(&mInfo, &profile, &profileLength) &&
(mInProfile = qcms_profile_from_memory(profile, profileLength)) != NULL) {
free(profile);
@@ -448,35 +434,16 @@ nsresult nsJPEGDecoder::ProcessData(cons
* Don't allocate a giant and superfluous memory buffer
* when the image is a sequential JPEG.
*/
mInfo.buffered_image = jpeg_has_multiple_scans(&mInfo);
/* Used to set up image size so arrays can be allocated */
jpeg_calc_output_dimensions(&mInfo);
- mObserver->OnStartDecode(nsnull);
-
- /* verify that the width and height of the image are the same as
- * the container we're about to put things in to.
- * XXX it might not matter maybe we should just resize the image.
- */
- PRInt32 width, height;
- mImage->GetWidth(&width);
- mImage->GetHeight(&height);
- if (width == 0 && height == 0) {
- mImage->Init(mInfo.image_width, mInfo.image_height, mObserver);
- } else if ((width != (PRInt32)mInfo.image_width) || (height != (PRInt32)mInfo.image_height)) {
- mState = JPEG_ERROR;
- return NS_ERROR_UNEXPECTED;
- }
-
- mImage->Init(mInfo.image_width, mInfo.image_height, mObserver);
-
- mObserver->OnStartContainer(nsnull, mImage);
// Use EnsureCleanFrame so we don't create a new frame if we're being
// reused for e.g. multipart/x-replace
PRUint32 imagelength;
if (NS_FAILED(mImage->EnsureCleanFrame(0, 0, 0, mInfo.image_width, mInfo.image_height,
gfxASurface::ImageFormatRGB24,
&mImageData, &imagelength))) {
mState = JPEG_ERROR;
@@ -484,17 +451,18 @@ nsresult nsJPEGDecoder::ProcessData(cons
("} (could not initialize image frame)"));
return NS_ERROR_OUT_OF_MEMORY;
}
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
(" JPEGDecoderAccounting: nsJPEGDecoder::ProcessData -- created image frame with %ux%u pixels",
mInfo.image_width, mInfo.image_height));
- mObserver->OnStartFrame(nsnull, 0);
+ if (mObserver)
+ mObserver->OnStartFrame(nsnull, 0);
mState = JPEG_START_DECOMPRESS;
}
case JPEG_START_DECOMPRESS:
{
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_START_DECOMPRESS case");
/* Step 4: set parameters for decompression */
@@ -616,38 +584,28 @@ nsresult nsJPEGDecoder::ProcessData(cons
}
mState = JPEG_DONE;
}
}
case JPEG_DONE:
{
- nsresult result;
-
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_DONE case");
/* Step 7: Finish decompression */
if (jpeg_finish_decompress(&mInfo) == FALSE) {
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
("} (I/O suspension after jpeg_finish_decompress() - DONE)"));
return NS_OK; /* I/O suspension */
}
mState = JPEG_SINK_NON_JPEG_TRAILER;
- result = mImage->RestoreDataDone();
- if (NS_FAILED (result)) {
- mState = JPEG_ERROR;
- PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
- ("} (could not mark image container with RestoreDataDone)"));
- return result;
- }
-
/* we're done dude */
break;
}
case JPEG_SINK_NON_JPEG_TRAILER:
PR_LOG(gJPEGlog, PR_LOG_DEBUG,
("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this));
break;
@@ -659,16 +617,36 @@ nsresult nsJPEGDecoder::ProcessData(cons
break;
}
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
("} (end of function)"));
return NS_OK;
}
+void
+nsJPEGDecoder::NotifyDone(PRBool aSuccess)
+{
+ // We should only be called once
+ NS_ABORT_IF_FALSE(!mNotifiedDone, "calling NotifyDone twice!");
+
+ // Notify
+ if (mObserver)
+ mObserver->OnStopFrame(nsnull, 0);
+ if (aSuccess)
+ mImage->DecodingComplete();
+ if (mObserver) {
+ mObserver->OnStopContainer(nsnull, mImage);
+ mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
+ nsnull);
+ }
+
+ // Mark that we've been called
+ mNotifiedDone = PR_TRUE;
+}
nsresult
nsJPEGDecoder::OutputScanlines(PRBool* suspend)
{
*suspend = PR_FALSE;
const PRUint32 top = mInfo.output_scanline;
nsresult rv = NS_OK;
@@ -754,17 +732,18 @@ nsJPEGDecoder::OutputScanlines(PRBool* s
*imageRow++ = GFX_PACKED_PIXEL(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]);
sampleRow += 3;
}
}
if (top != mInfo.output_scanline) {
nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top);
rv = mImage->FrameUpdated(0, r);
- mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
+ if (mObserver)
+ mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
}
return rv;
}
/* Override the standard error method in the IJG JPEG decoder code. */
METHODDEF(void)
@@ -971,27 +950,22 @@ fill_input_buffer (j_decompress_ptr jd)
* data has been read to clean up JPEG source manager. NOT called by
* jpeg_abort() or jpeg_destroy().
*/
METHODDEF(void)
term_source (j_decompress_ptr jd)
{
nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
- if (decoder->mObserver) {
- decoder->mObserver->OnStopFrame(nsnull, 0);
- decoder->mObserver->OnStopContainer(nsnull, decoder->mImage);
- decoder->mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
- }
+ // This function shouldn't be called if we ran into an error
+ NS_ABORT_IF_FALSE(!decoder->mError,
+ "Calling term_source on a JPEG with mError=true!");
- PRBool multipart = PR_FALSE;
- if (decoder->mImageLoad)
- decoder->mImageLoad->GetIsMultiPartChannel(&multipart);
- if (!multipart)
- decoder->mImage->DecodingComplete();
+ // Notify
+ decoder->NotifyDone(/* aSuccess = */ PR_TRUE);
}
/**************** YCbCr -> Cairo's RGB24/ARGB32 conversion: most common case **************/
/*
* YCbCr is defined per CCIR 601-1, except that Cb and Cr are
* normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
--- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h
+++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -41,17 +42,16 @@
#define nsJPEGDecoder_h__
#include "imgIDecoder.h"
#include "nsCOMPtr.h"
#include "imgIContainer.h"
#include "imgIDecoderObserver.h"
-#include "imgILoad.h"
#include "nsIInputStream.h"
#include "nsIPipe.h"
#include "qcms.h"
extern "C" {
#include "jpeglib.h"
}
@@ -85,33 +85,34 @@ class nsJPEGDecoder : public imgIDecoder
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IMGIDECODER
nsJPEGDecoder();
virtual ~nsJPEGDecoder();
- nsresult ProcessData(const char *data, PRUint32 count, PRUint32 *writeCount);
+ nsresult ProcessData(const char *data, PRUint32 count);
+ void NotifyDone(PRBool aSuccess);
protected:
nsresult OutputScanlines(PRBool* suspend);
public:
nsCOMPtr<imgIContainer> mImage;
- nsCOMPtr<imgILoad> mImageLoad;
+ nsCOMPtr<imgIDecoderObserver> mObserver;
- nsCOMPtr<imgIDecoderObserver> mObserver;
+ PRUint32 mFlags;
PRUint8 *mImageData;
struct jpeg_decompress_struct mInfo;
struct jpeg_source_mgr mSourceMgr;
decoder_error_mgr mErr;
jstate mState;
- nsresult mError;
+ PRBool mError;
PRUint32 mBytesToSkip;
const JOCTET *mSegment; // The current segment we are decoding from
PRUint32 mSegmentLen; // amount of data in mSegment
JOCTET *mBackBuffer;
PRUint32 mBackBufferLen; // Offset of end of active backtrack data
@@ -120,11 +121,12 @@ public:
JOCTET *mProfile;
PRUint32 mProfileLength;
qcms_profile *mInProfile;
qcms_transform *mTransform;
PRPackedBool mReading;
+ PRPackedBool mNotifiedDone;
};
#endif // nsJPEGDecoder_h__
--- a/modules/libpr0n/decoders/png/nsPNGDecoder.cpp
+++ b/modules/libpr0n/decoders/png/nsPNGDecoder.cpp
@@ -19,16 +19,17 @@
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Andrew Smith
* Federico Mena-Quintero <federico@novell.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -66,39 +67,57 @@ static void PNGAPI end_callback(png_stru
static void PNGAPI error_callback(png_structp png_ptr, png_const_charp error_msg);
static void PNGAPI warning_callback(png_structp png_ptr, png_const_charp warning_msg);
#ifdef PR_LOGGING
static PRLogModuleInfo *gPNGLog = PR_NewLogModule("PNGDecoder");
static PRLogModuleInfo *gPNGDecoderAccountingLog = PR_NewLogModule("PNGDecoderAccounting");
#endif
+/* limit image dimensions (bug #251381) */
+#define MOZ_PNG_MAX_DIMENSION 1000000L
+
+// For header-only decodes
+#define WIDTH_OFFSET 16
+#define HEIGHT_OFFSET (WIDTH_OFFSET + 4)
+#define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4)
+
+// This is defined in the PNG spec as an invariant. We use it to
+// do manual validation without libpng.
+static const PRUint8 pngSignatureBytes[] =
+ { 137, 80, 78, 71, 13, 10, 26, 10 };
+
+
NS_IMPL_ISUPPORTS1(nsPNGDecoder, imgIDecoder)
nsPNGDecoder::nsPNGDecoder() :
mPNG(nsnull), mInfo(nsnull),
mCMSLine(nsnull), interlacebuf(nsnull),
mInProfile(nsnull), mTransform(nsnull),
- mChannels(0), mError(PR_FALSE), mFrameIsHidden(PR_FALSE)
+ mHeaderBuf(nsnull), mHeaderBytesRead(0),
+ mChannels(0), mError(PR_FALSE), mFrameIsHidden(PR_FALSE),
+ mNotifiedDone(PR_FALSE)
{
}
nsPNGDecoder::~nsPNGDecoder()
{
if (mCMSLine)
nsMemory::Free(mCMSLine);
if (interlacebuf)
nsMemory::Free(interlacebuf);
if (mInProfile) {
qcms_profile_release(mInProfile);
/* mTransform belongs to us only if mInProfile is non-null */
if (mTransform)
qcms_transform_release(mTransform);
}
+ if (mHeaderBuf)
+ nsMemory::Free(mHeaderBuf);
}
// CreateFrame() is used for both simple and animated images
void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
PRInt32 width, PRInt32 height,
gfxASurface::gfxImageFormat format)
{
PRUint32 imageDataLength;
@@ -186,29 +205,34 @@ void nsPNGDecoder::EndImageFrame()
mImage->SetFrameHasNoAlpha(numFrames - 1);
if (NS_FAILED(mImage->FrameUpdated(numFrames - 1, mFrameRect))) {
mError = PR_TRUE;
// allow the call out to the observers.
}
PRUint32 curFrame;
mImage->GetCurrentFrameIndex(&curFrame);
- mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &mFrameRect);
+ if (mObserver)
+ mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &mFrameRect);
}
mImage->EndFrameDecode(numFrames - 1);
if (mObserver)
mObserver->OnStopFrame(nsnull, numFrames - 1);
}
/** imgIDecoder methods **/
-/* void init (in imgILoad aLoad); */
-NS_IMETHODIMP nsPNGDecoder::Init(imgILoad *aLoad)
+/* void init (in imgIContainer aImage,
+ imgIDecoderObserver aObserver,
+ unsigned long aFlags); */
+NS_IMETHODIMP nsPNGDecoder::Init(imgIContainer *aImage,
+ imgIDecoderObserver *aObserver,
+ PRUint32 aFlags)
{
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
static png_byte color_chunks[]=
{ 99, 72, 82, 77, '\0', /* cHRM */
105, 67, 67, 80, '\0'}; /* iCCP */
static png_byte unused_chunks[]=
{ 98, 75, 71, 68, '\0', /* bKGD */
104, 73, 83, 84, '\0', /* hIST */
@@ -219,20 +243,33 @@ NS_IMETHODIMP nsPNGDecoder::Init(imgILoa
112, 72, 89, 115, '\0', /* pHYs */
115, 66, 73, 84, '\0', /* sBIT */
115, 80, 76, 84, '\0', /* sPLT */
116, 69, 88, 116, '\0', /* tEXt */
116, 73, 77, 69, '\0', /* tIME */
122, 84, 88, 116, '\0'}; /* zTXt */
#endif
- mImageLoad = aLoad;
- mObserver = do_QueryInterface(aLoad); // we're holding 2 strong refs to the request.
+ mImage = aImage;
+ mObserver = aObserver;
+ mFlags = aFlags;
+
+ // Fire OnStartDecode at init time to support bug 512435
+ if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
+ mObserver->OnStartDecode(nsnull);
- /* do png init stuff */
+ // For header-only decodes, we only need a small buffer
+ if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) {
+ mHeaderBuf = (PRUint8 *)nsMemory::Alloc(BYTES_NEEDED_FOR_DIMENSIONS);
+ if (!mHeaderBuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+ return NS_OK;
+ }
+
+ /* For full decodes, do png init stuff */
/* Initialize the container's source image header. */
/* Always decode to 24 bit pixdepth */
mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, error_callback, warning_callback);
if (!mPNG) {
return NS_ERROR_OUT_OF_MEMORY;
@@ -253,129 +290,169 @@ NS_IMETHODIMP nsPNGDecoder::Init(imgILoa
(int)sizeof(unused_chunks)/5);
#endif
/* use this as libpng "progressive pointer" (retrieve in callbacks) */
png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this),
info_callback, row_callback, end_callback);
- /* The image container may already exist if it is reloading itself from us.
- * Check that it has the same width/height; otherwise create a new container.
- */
- mImageLoad->GetImage(getter_AddRefs(mImage));
- if (!mImage) {
- mImage = do_CreateInstance("@mozilla.org/image/container;2");
- if (!mImage)
- return NS_ERROR_OUT_OF_MEMORY;
-
- mImageLoad->SetImage(mImage);
- if (NS_FAILED(mImage->SetDiscardable("image/png"))) {
- PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
- ("PNGDecoderAccounting: info_callback(): failed to set image container %p as discardable",
- mImage.get()));
- return NS_ERROR_FAILURE;
- }
- }
-
return NS_OK;
}
/* void close (); */
-NS_IMETHODIMP nsPNGDecoder::Close()
+NS_IMETHODIMP nsPNGDecoder::Close(PRUint32 aFlags)
{
if (mPNG)
png_destroy_read_struct(&mPNG, mInfo ? &mInfo : NULL, NULL);
- if (mImage) { // mImage could be null in the case of an error
- nsresult result = mImage->RestoreDataDone();
- if (NS_FAILED(result)) {
- PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
- ("PNGDecoderAccounting: nsPNGDecoder::Close(): failure in RestoreDataDone() for image container %p",
- mImage.get()));
+ // If we're a full/success decode but haven't sent stop notifications yet,
+ // we didn't get all the data we needed. Send error notifications.
+ if (!(aFlags & CLOSE_FLAG_DONTNOTIFY) &&
+ !(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
+ !mNotifiedDone)
+ NotifyDone(/* aSuccess = */ PR_FALSE);
- mError = PR_TRUE;
- return result;
- }
-
- PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
- ("PNGDecoderAccounting: nsPNGDecoder::Close(): image container %p is now with RestoreDataDone",
- mImage.get()));
- }
+ mImage = nsnull;
return NS_OK;
}
/* void flush (); */
NS_IMETHODIMP nsPNGDecoder::Flush()
{
return NS_OK;
}
+// We make this a method to get the benefit of the 'this' parameter
+NS_METHOD
+nsPNGDecoder::ProcessData(unsigned char* aBuffer, PRUint32 aCount)
+{
+ // We use gotos, so we need to declare variables here
+ nsresult rv;
+ PRUint32 width = 0;
+ PRUint32 height = 0;
+
+ // No forgiveness if we previously hit an error
+ if (mError)
+ goto error;
+
+ // If we only want width/height, we don't need to go through libpng
+ if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) {
+
+ // Are we done?
+ if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS)
+ return NS_OK;
+
+ // Read data into our header buffer
+ PRUint32 bytesToRead = PR_MIN(aCount, BYTES_NEEDED_FOR_DIMENSIONS - mHeaderBytesRead);
+ memcpy(mHeaderBuf + mHeaderBytesRead, aBuffer, bytesToRead);
+ mHeaderBytesRead += bytesToRead;
+
+ // If we're done now, verify the data and set up the container
+ if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS) {
+
+ // Check that the signature bytes are right
+ if (memcmp(mHeaderBuf, pngSignatureBytes, sizeof(pngSignatureBytes)))
+ goto error;
+
+ // Grab the width and height, accounting for endianness (thanks libpng!)
+ width = png_get_uint_32(mHeaderBuf + WIDTH_OFFSET);
+ height = png_get_uint_32(mHeaderBuf + HEIGHT_OFFSET);
+
+ // Too big?
+ if ((width > MOZ_PNG_MAX_DIMENSION) || (height > MOZ_PNG_MAX_DIMENSION))
+ goto error;
+
+ // Set the size
+ rv = mImage->SetSize(width, height);
+ if (NS_FAILED(rv))
+ goto error;
+
+ // Notify the observer that the container is up
+ if (mObserver)
+ mObserver->OnStartContainer(nsnull, mImage);
+ }
+ }
+
+ // Otherwise, we're doing a standard decode
+ else {
+
+ // libpng uses setjmp/longjmp for error handling - set the buffer
+ if (setjmp(mPNG->jmpbuf)) {
+ png_destroy_read_struct(&mPNG, &mInfo, NULL);
+ goto error;
+ }
+
+ // Pass the data off to libpng
+ png_process_data(mPNG, mInfo, aBuffer, aCount);
+
+ }
+
+ return NS_OK;
+
+ // Consolidate error handling
+ error:
+ mError = PR_TRUE;
+ return NS_ERROR_FAILURE;
+}
+
+void
+nsPNGDecoder::NotifyDone(PRBool aSuccess)
+{
+ // We should only be called once
+ NS_ABORT_IF_FALSE(!mNotifiedDone, "Calling NotifyDone twice!");
+
+ // Notify
+ if (!mFrameIsHidden)
+ EndImageFrame();
+ if (aSuccess)
+ mImage->DecodingComplete();
+ if (mObserver) {
+ mObserver->OnStopContainer(nsnull, mImage);
+ mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
+ nsnull);
+ }
+
+ // Mark that we've been called
+ mNotifiedDone = PR_TRUE;
+}
static NS_METHOD ReadDataOut(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount)
{
+ // Grab the decoder
+ NS_ENSURE_ARG_POINTER(closure);
nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(closure);
- if (decoder->mError) {
- *writeCount = 0;
- return NS_ERROR_FAILURE;
- }
-
- // we force to add even erroneous data to restore halfway frame information
- // later - bug 441563
- nsresult result = decoder->mImage->AddRestoreData(const_cast<char *>(fromRawSegment), count);
- if (NS_FAILED (result)) {
- PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
- ("PNGDecoderAccounting: ReadDataOut(): failed to add restore data to image container %p",
- decoder->mImage.get()));
-
- decoder->mError = PR_TRUE;
- *writeCount = 0;
- return result;
- }
+ // We always read everything
+ *writeCount = count;
- // we need to do the setjmp here otherwise bad things will happen
- if (setjmp(decoder->mPNG->jmpbuf)) {
- png_destroy_read_struct(&decoder->mPNG, &decoder->mInfo, NULL);
-
- decoder->mError = PR_TRUE;
- *writeCount = 0;
- return NS_ERROR_FAILURE;
- }
- png_process_data(decoder->mPNG, decoder->mInfo,
- reinterpret_cast<unsigned char *>(const_cast<char *>(fromRawSegment)), count);
-
- PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
- ("PNGDecoderAccounting: ReadDataOut(): Added restore data to image container %p",
- decoder->mImage.get()));
-
- *writeCount = count;
- return NS_OK;
+ // Twiddle the types, then process the data
+ char *unConst = const_cast<char *>(fromRawSegment);
+ unsigned char *buffer = reinterpret_cast<unsigned char *>(unConst);
+ return decoder->ProcessData(buffer, count);
}
-/* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
-NS_IMETHODIMP nsPNGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
+/* writeFrom (in nsIInputStream inStr, in unsigned long count); */
+NS_IMETHODIMP nsPNGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count)
{
NS_ASSERTION(inStr, "Got a null input stream!");
- nsresult rv;
-
+ // Decode, watching for errors
+ nsresult rv = NS_OK;
+ PRUint32 ignored;
if (!mError)
- rv = inStr->ReadSegments(ReadDataOut, this, count, _retval);
-
- if (mError) {
- *_retval = 0;
+ rv = inStr->ReadSegments(ReadDataOut, this, count, &ignored);
+ if (mError || NS_FAILED(rv))
rv = NS_ERROR_FAILURE;
- }
return rv;
}
// Sets up gamma pre-correction in libpng before our callback gets called.
// We need to do this if we don't end up with a CMS profile.
static void
PNGDoGammaCorrection(png_structp png_ptr, png_infop info_ptr)
@@ -500,26 +577,33 @@ info_callback(png_structp png_ptr, png_i
png_uint_32 width, height;
int bit_depth, color_type, interlace_type, compression_type, filter_type;
unsigned int channels;
png_bytep trans = NULL;
int num_trans = 0;
nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
+ nsresult rv;
/* always decode to 24-bit RGB or 32-bit RGBA */
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, &compression_type, &filter_type);
- /* limit image dimensions (bug #251381) */
-#define MOZ_PNG_MAX_DIMENSION 1000000L
+ /* Are we too big? */
if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION)
longjmp(decoder->mPNG->jmpbuf, 1);
-#undef MOZ_PNG_MAX_DIMENSION
+
+ // Set the size and notify that the container is set up
+ rv = decoder->mImage->SetSize(width, height);
+ if (NS_FAILED(rv)) {
+ longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_UNEXPECTED
+ }
+ if (decoder->mObserver)
+ decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
@@ -611,35 +695,16 @@ info_callback(png_structp png_ptr, png_i
}
}
}
} else {
alpha_bits = 8;
}
}
- if (decoder->mObserver)
- decoder->mObserver->OnStartDecode(nsnull);
-
- /* The image container may already exist if it is reloading itself from us.
- * Check that it has the same width/height; otherwise create a new container.
- */
- PRInt32 containerWidth, containerHeight;
- decoder->mImage->GetWidth(&containerWidth);
- decoder->mImage->GetHeight(&containerHeight);
- if (containerWidth == 0 && containerHeight == 0) {
- // the image hasn't been inited yet
- decoder->mImage->Init(width, height, decoder->mObserver);
- } else if (containerWidth != PRInt32(width) || containerHeight != PRInt32(height)) {
- longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_UNEXPECTED
- }
-
- if (decoder->mObserver)
- decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
-
if (channels == 1 || channels == 3)
decoder->format = gfxASurface::ImageFormatRGB24;
else if (channels == 2 || channels == 4)
decoder->format = gfxASurface::ImageFormatARGB32;
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL))
png_set_progressive_frame_fn(png_ptr, frame_info_callback, NULL);
@@ -649,18 +714,19 @@ info_callback(png_structp png_ptr, png_i
decoder->CreateFrame(0, 0, width, height, decoder->format);
}
if (decoder->mTransform &&
(channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) {
PRUint32 bpp[] = { 0, 3, 4, 3, 4 };
decoder->mCMSLine =
(PRUint8 *)nsMemory::Alloc(bpp[channels] * width);
- if (!decoder->mCMSLine)
+ if (!decoder->mCMSLine) {
longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_OUT_OF_MEMORY
+ }
}
if (interlace_type == PNG_INTERLACE_ADAM7) {
if (height < PR_INT32_MAX / (width * channels))
decoder->interlacebuf = (PRUint8 *)nsMemory::Alloc(channels * width * height);
if (!decoder->interlacebuf) {
longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_OUT_OF_MEMORY
}
@@ -795,17 +861,18 @@ row_callback(png_structp png_ptr, png_by
// Only do incremental image display for the first frame
nsIntRect r(0, row_num, width, 1);
if (NS_FAILED(decoder->mImage->FrameUpdated(numFrames - 1, r))) {
decoder->mError = PR_TRUE; /* bail */
return;
}
PRUint32 curFrame;
decoder->mImage->GetCurrentFrameIndex(&curFrame);
- decoder->mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &r);
+ if (decoder->mObserver)
+ decoder->mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &r);
}
}
}
// got the header of a new frame that's coming
void
frame_info_callback(png_structp png_ptr, png_uint_32 frame_num)
{
@@ -839,31 +906,27 @@ end_callback(png_structp png_ptr, png_in
* had in the header, although some data may have been added
* to the comments and time fields.
*
* Most people won't do much here, perhaps setting a flag that
* marks the image as finished.
*/
nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
+
+ // We shouldn't get here if we've hit an error
+ NS_ABORT_IF_FALSE(!decoder->mError, "Finishing up PNG but hit error!");
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
PRInt32 num_plays = png_get_num_plays(png_ptr, info_ptr);
decoder->mImage->SetLoopCount(num_plays - 1);
}
-
- if (!decoder->mFrameIsHidden)
- decoder->EndImageFrame();
-
- decoder->mImage->DecodingComplete();
- if (decoder->mObserver) {
- decoder->mObserver->OnStopContainer(nsnull, decoder->mImage);
- decoder->mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
- }
+ // Send final notifications
+ decoder->NotifyDone(/* aSuccess = */ PR_TRUE);
}
void
error_callback(png_structp png_ptr, png_const_charp error_msg)
{
PR_LOG(gPNGLog, PR_LOG_ERROR, ("libpng error: %s\n", error_msg));
longjmp(png_ptr->jmpbuf, 1);
--- a/modules/libpr0n/decoders/png/nsPNGDecoder.h
+++ b/modules/libpr0n/decoders/png/nsPNGDecoder.h
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -39,17 +40,16 @@
#ifndef nsPNGDecoder_h__
#define nsPNGDecoder_h__
#include "imgIDecoder.h"
#include "imgIContainer.h"
#include "imgIDecoderObserver.h"
-#include "imgILoad.h"
#include "gfxASurface.h"
#include "nsCOMPtr.h"
#include "png.h"
#include "qcms.h"
@@ -71,31 +71,39 @@ public:
virtual ~nsPNGDecoder();
void CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
PRInt32 width, PRInt32 height,
gfxASurface::gfxImageFormat format);
void SetAnimFrameInfo();
void EndImageFrame();
+ NS_METHOD ProcessData(unsigned char* aBuffer, PRUint32 aCount);
+ void NotifyDone(PRBool aSuccess);
public:
nsCOMPtr<imgIContainer> mImage;
- nsCOMPtr<imgILoad> mImageLoad;
- nsCOMPtr<imgIDecoderObserver> mObserver; // this is just qi'd from mRequest for speed
+ nsCOMPtr<imgIDecoderObserver> mObserver;
+ PRUint32 mFlags;
png_structp mPNG;
png_infop mInfo;
nsIntRect mFrameRect;
PRUint8 *mCMSLine;
PRUint8 *interlacebuf;
PRUint8 *mImageData;
qcms_profile *mInProfile;
qcms_transform *mTransform;
gfxASurface::gfxImageFormat format;
+
+ // For header-only decodes
+ PRUint8 *mHeaderBuf;
+ PRUint32 mHeaderBytesRead;
+
PRUint8 mChannels;
PRPackedBool mError;
PRPackedBool mFrameHasNoAlpha;
PRPackedBool mFrameIsHidden;
+ PRPackedBool mNotifiedDone;
};
#endif // nsPNGDecoder_h__
--- a/modules/libpr0n/public/Makefile.in
+++ b/modules/libpr0n/public/Makefile.in
@@ -49,16 +49,15 @@ EXPORTS = ImageErrors.h ImageLogging.h
XPIDLSRCS = \
imgICache.idl \
imgIContainer.idl \
imgIContainerObserver.idl \
imgIDecoder.idl \
imgIDecoderObserver.idl \
imgIEncoder.idl \
- imgILoad.idl \
imgILoader.idl \
imgIRequest.idl \
imgITools.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk
--- a/modules/libpr0n/public/imgIContainer.idl
+++ b/modules/libpr0n/public/imgIContainer.idl
@@ -19,16 +19,17 @@
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
* Federico Mena-Quintero <federico@novell.com>
* Joe Drew <joe@drew.ca>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -36,17 +37,17 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
-interface imgIContainerObserver;
+interface imgIDecoderObserver;
%{C++
#include "gfxImageSurface.h"
#include "gfxContext.h"
#include "gfxMatrix.h"
#include "gfxRect.h"
#include "gfxPattern.h"
#include "gfxASurface.h"
@@ -65,17 +66,17 @@ native gfxGraphicsFilter(gfxPattern::Gra
/**
* imgIContainer is the interface that represents an image. It allows
* access to frames as Thebes surfaces, and permits users to extract subregions
* as other imgIContainers. It also allows drawing of images on to Thebes
* contexts.
*
* Internally, imgIContainer also manages animation of images.
*/
-[scriptable, uuid(1bcf7a25-1356-47a8-bf80-e284989ea38f)]
+[scriptable, uuid(74c63935-b54f-43a4-9449-2e9c815e3bef)]
interface imgIContainer : nsISupports
{
/**
* The width of the container rectangle.
*/
readonly attribute PRInt32 width;
/**
@@ -90,67 +91,156 @@ interface imgIContainer : nsISupports
/**
* Whether the current frame is opaque; that is, needs the background painted
* behind it.
*/
readonly attribute boolean currentFrameIsOpaque;
/**
- * Get a surface for the current frame. This may be a platform-native,
- * optimized frame, so you cannot inspect its pixel data.
+ * Flags for imgIContainer operations.
+ *
+ * Meanings:
+ *
+ * FLAG_NONE: Lack of flags
+ *
+ * FLAG_SYNC_DECODE: Forces synchronous/non-progressive decode of all
+ * available data before the call returns.
*/
- [noscript] readonly attribute gfxASurface currentFrame;
+
+ const long FLAG_NONE = 0x0;
+ const long FLAG_SYNC_DECODE = 0x1;
/**
- * Create and return a new copy of the current frame that you can write to
+ * Constants for specifying various "special" frames.
+ *
+ * FRAME_FIRST: The first frame
+ * FRAME_CURRENT: The current frame
+ *
+ * FRAME_MAX_VALUE should be set to the value of the maximum constant above,
+ * as it is used for ensuring that a valid value was passed in.
+ */
+ const unsigned long FRAME_FIRST = 0;
+ const unsigned long FRAME_CURRENT = 1;
+ const unsigned long FRAME_MAX_VALUE = 1;
+
+ /**
+ * Get a surface for the given frame. This may be a platform-native,
+ * optimized surface, so you cannot inspect its pixel data.
+ *
+ * @param aWhichFrame Frame specifier of the FRAME_* variety.
+ * @param aFlags Flags of the FLAG_* variety
+ */
+ [noscript] gfxASurface getFrame(in PRUint32 aWhichFrame,
+ in PRUint32 aFlags);
+
+ /**
+ * Create and return a new copy of the given frame that you can write to
* and otherwise inspect the pixels of.
+ *
+ * @param aWhichFrame Frame specifier of the FRAME_* variety.
+ * @param aFlags Flags of the FLAG_* variety
*/
- [noscript] gfxImageSurface copyCurrentFrame();
+ [noscript] gfxImageSurface copyFrame(in PRUint32 aWhichFrame,
+ in PRUint32 aFlags);
/**
* Create a new imgContainer that contains only a single frame, which itself
- * contains a subregion of the current frame.
+ * contains a subregion of the given frame.
*
+ * @param aWhichFrame Frame specifier of the FRAME_* variety.
* @param aRect the area of the current frame to be duplicated in the
* returned imgContainer's frame.
+ * @param aFlags Flags of the FLAG_* variety
*/
- [noscript] imgIContainer extractCurrentFrame([const] in nsIntRect aRect);
+ [noscript] imgIContainer extractFrame(in PRUint32 aWhichFrame,
+ [const] in nsIntRect aRect,
+ in PRUint32 aFlags);
/**
* Draw the current frame on to the context specified.
*
* @param aContext The Thebex context to draw the image to.
* @param aFilter The filter to be used if we're scaling the image.
* @param aUserSpaceToImageSpace The transformation from user space (e.g.,
* appunits) to image space.
* @param aFill The area in the context to draw pixels to. Image will be
* automatically tiled as necessary.
* @param aSubimage The area of the image, in pixels, that we are allowed to
* sample from.
+ * @param aFlags Flags of the FLAG_* variety
*/
[noscript] void draw(in gfxContext aContext, in gfxGraphicsFilter aFilter,
in gfxMatrix aUserSpaceToImageSpace, in gfxRect aFill,
- in nsIntRect aSubimage);
+ in nsIntRect aSubimage, in PRUint32 aFlags);
+
+ /*
+ * Ensures that an image is decoding. Calling this function guarantees that
+ * the image will at some point fire off decode notifications. Calling draw(),
+ * getFrame(), copyFrame(), or extractCurrentFrame() triggers the same
+ * mechanism internally. Thus, if you want to be sure that the image will be
+ * decoded but don't want to access it until then, you must call
+ * requestDecode().
+ */
+ void requestDecode();
+ /**
+ * Increments the lock count on the image. An image will not be discarded
+ * as long as the lock count is nonzero. Note that it is still possible for
+ * the image to be undecoded if decode-on-draw is enabled and the image
+ * was never drawn.
+ *
+ * Upon instantiation images have a lock count of zero.
+ */
+ void lockImage();
+
+ /**
+ * Decreases the lock count on the image. If the lock count drops to zero,
+ * the image is allowed to discard its frame data to save memory.
+ *
+ * Upon instantiation images have a lock count of zero. It is an error to
+ * call this method without first having made a matching lockImage() call.
+ * In other words, the lock count is not allowed to be negative.
+ */
+ void unlockImage();
/************ Internal libpr0n use only below here. *****************/
/**
- * Create a new \a aWidth x \a aHeight sized image container.
+ * Flags for imgIContainer initialization.
+ *
+ * Meanings:
+ *
+ * INIT_FLAG_NONE: Lack of flags
+ *
+ * INIT_FLAG_DISCARDABLE: The container should be discardable
+ *
+ * INIT_FLAG_DECODE_ON_DRAW: The container should decode on draw rather than
+ * decoding on load.
*
- * @param aWidth The width of the container in which all the
- * frames will fit.
- * @param aHeight The height of the container in which all the
- * frames will fit.
- * @param aObserver Observer to send animation notifications to.
+ * INIT_FLAG_MULTIPART: The container will be used to display a stream of
+ * images in a multipart channel. If this flag is set, INIT_FLAG_DISCARDABLE
+ * and INIT_FLAG_DECODE_ON_DRAW must not be set.
*/
- void init(in PRInt32 aWidth,
- in PRInt32 aHeight,
- in imgIContainerObserver aObserver);
+
+ const long INIT_FLAG_NONE = 0x0;
+ const long INIT_FLAG_DISCARDABLE = 0x1;
+ const long INIT_FLAG_DECODE_ON_DRAW = 0x2;
+ const long INIT_FLAG_MULTIPART = 0x4;
+
+ /**
+ * Creates a new image container.
+ *
+ * @param aObserver Observer to send decoder and animation notifications to.
+ * @param aMimeType The mimetype of the image.
+ * @param aFlags Initialization flags of the INIT_FLAG_* variety.
+ */
+ void init(in imgIDecoderObserver aObserver,
+ in string aMimeType,
+ in PRUint32 aFlags);
/**
* "Disposal" method indicates how the image should be handled before the
* subsequent image is displayed.
* Don't change these without looking at the implementations using them,
* struct gif_struct::disposal_method and gif_write() in particular.
*/
const long kDisposeClearAll = -1; // Clear the whole image, revealing
@@ -196,30 +286,33 @@ interface imgIContainer : nsISupports
readonly attribute unsigned long currentFrameIndex;
/**
* The total number of frames in this image.
*/
readonly attribute unsigned long numFrames;
/**
- * Get the size, in bytes, of a particular frame's image data.
+ * The size, in bytes, occupied by the significant data portions of the image.
+ * This includes both compressed source data and decoded frames.
*/
- unsigned long getFrameImageDataLength(in unsigned long framenumber);
-
- void getFrameColormap(in unsigned long framenumber,
- [array, size_is(paletteLength)] out PRUint32 paletteData,
- out unsigned long paletteLength);
+ readonly attribute unsigned long dataSize;
void setFrameDisposalMethod(in unsigned long framenumber, in PRInt32 aDisposalMethod);
void setFrameBlendMethod(in unsigned long framenumber, in PRInt32 aBlendMethod);
void setFrameTimeout(in unsigned long framenumber, in PRInt32 aTimeout);
void setFrameHasNoAlpha(in unsigned long framenumber);
/**
+ * Sets the size of the container. This should only be called by the decoder. This function may be called multiple
+ * times, but will throw an error if subsequent calls do not match the first.
+ */
+ [noscript] void setSize(in long aWidth, in long aHeight);
+
+ /**
* Create or re-use a frame at index aFrameNum. It is an error to call this with aFrameNum not in the range [0, numFrames].
*/
[noscript] void ensureCleanFrame(in unsigned long aFramenum, in PRInt32 aX, in PRInt32 aY, in PRInt32 aWidth, in PRInt32 aHeight, in gfxImageFormat aFormat,
[array, size_is(imageLength)] out PRUint8 imageData, out unsigned long imageLength);
/**
* Adds to the end of the list of frames.
*/
@@ -244,14 +337,23 @@ interface imgIContainer : nsISupports
void resetAnimation();
/**
* number of times to loop the image.
* @note -1 means forever.
*/
attribute long loopCount;
- /* Methods to discard uncompressed images and restore them again */
- [noscript] void setDiscardable(in string aMimeType);
- [noscript] void addRestoreData([array, size_is(aCount), const] in char data,
+ /* Add compressed source data to the imgContainer.
+ *
+ * The decoder will use this data, either immediately or at draw time, do
+ * decode the image.
+ */
+ [noscript] void addSourceData([array, size_is(aCount), const] in char data,
in unsigned long aCount);
- [noscript] void restoreDataDone();
+
+ /* Called after the all the source data has been added with addSourceData. */
+ [noscript] void sourceDataComplete();
+
+ /* Called for multipart images when there's a new source image to add. */
+ [noscript] void newSourceData();
+
};
--- a/modules/libpr0n/public/imgIDecoder.idl
+++ b/modules/libpr0n/public/imgIDecoder.idl
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -35,58 +36,88 @@
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIInputStream;
-interface imgILoad;
+interface imgIContainer;
+interface imgIDecoderObserver;
/**
* imgIDecoder interface
*
* @author Stuart Parmenter <pavlov@netscape.com>
* @version 0.2
* @see imagelib2
*/
-[scriptable, uuid(9eebf43a-1dd1-11b2-953e-f1782f4cbad3)]
+[scriptable, uuid(139c6f97-cd2f-4a4f-890d-8d9f4a693682)]
interface imgIDecoder : nsISupports
{
/**
- * Initialize an image decoder.
- * @param aRequest the request that owns the decoder.
+ * Bits that can be passed to the decoder to affect decoding.
+ * @name decodeflags
+ *
+ * Meanings:
+ *
+ * DECODER_FLAG_NONE: No flags
*
- * @note The decode should QI \a aLoad to an imgIDecoderObserver
- * and should send decoder notifications to the request.
- * The decoder should always pass NULL as the first two parameters to
- * all of the imgIDecoderObserver APIs.
+ * DECODER_FLAG_HEADERONLY: Read basic data from the image in order to
+ * set up the image container, but don't read any actual image data.
*/
- void init(in imgILoad aLoad);
+ const long DECODER_FLAG_NONE = 0x0;
+ const long DECODER_FLAG_HEADERONLY = 0x1;
+
+ /**
+ * Initialize an image decoder.
+ * @param aContainer The image container to decode to.
+ * @param aObserver The observer for decode notification events.
+ * @param aFlags Flags for the decoder
+ *
+ * @note The decoder should always pass NULL as the
+ * first two parameters to all of the imgIDecoderObserver APIs.
+ */
+ void init(in imgIContainer aImage, in imgIDecoderObserver aObserver,
+ in unsigned long aFlags);
/**
- * Closes the stream.
+ * Closes the stream.
+ * @param aFlags Close flags of the CLOSE_FLAG_* Variety
+ *
+ * Resources are always freed with this call. If notifications are sent,
+ * OnStopDecode is guaranteed to be called if it hasn't been called already.
+ *
+ * CLOSE_FLAG_DONTNOTIFY - Don't send any observer notifications, and don't
+ * call imgIContainer::decodingComplete().
*/
- void close();
+ const long CLOSE_FLAG_DONTNOTIFY = 0x01;
+ void close(in PRUint32 aFlags);
/**
* Flushes the stream.
*/
void flush();
/**
* Writes data into the stream from an input stream.
+ *
+ * For Header-Only decodes, OnStartContainer is the only notification
+ * fired.
+ *
+ * If a decoding error occurs, an internal flag is set and an error is
+ * returned. Each subsequent call to writeFrom will fail immediately
+ * for the lifetime of the decoder. Shutdown notifications of the OnStopX
+ * variety, as well as DecodingComplete(), are guaranteed not to be called
+ * if a decoding error occurs.
+ *
* Implementer's note: This method is defined by this interface in order
* to allow the output stream to efficiently copy the data from the input
* stream into its internal buffer (if any). If this method was provide
* as an external facility, a separate char* buffer would need to be used
* in order to call the output stream's other Write method.
* @param fromStream the stream from which the data is read
* @param count the maximun number of bytes to write
- * @return aWriteCount out parameter to hold the number of
- * bytes written. if an error occurs, the writecount
- * is undefined
*/
- unsigned long writeFrom(in nsIInputStream inStr,
- in unsigned long count);
+ void writeFrom(in nsIInputStream inStr, in unsigned long count);
};
--- a/modules/libpr0n/public/imgIDecoderObserver.idl
+++ b/modules/libpr0n/public/imgIDecoderObserver.idl
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -47,74 +48,127 @@ interface imgIContainer;
%}
/**
* imgIDecoderObserver interface
*
* This interface is used both for observing imgIDecoder objects and for
* observing imgIRequest objects. In the former case, aRequest is
* always null.
- * XXXldb The two functions should probably be split.
+ *
+ * We make the distinction here between "load" and "decode" notifications. Load
+ * notifications are fired as the image is loaded from the network or
+ * filesystem. Decode notifications are fired as the image is decoded. If an
+ * image is decoded on load and not visibly discarded, decode notifications are
+ * nested logically inside load notifications as one might expect. However, with
+ * decode-on-draw, the set of decode notifications can come completely _after_
+ * the load notifications, and can come multiple times if the image is
+ * discardable. Moreover, they can be interleaved in various ways. In general,
+ * any presumed ordering between load and decode notifications should not be
+ * relied upon.
+ *
+ * Decode notifications may or may not be synchronous, depending on the
+ * situation. If imgIDecoder::FLAG_SYNC_DECODE is passed to a function that
+ * triggers a decode, all notifications that can be generated from the currently
+ * loaded data fire before the call returns. If FLAG_SYNC_DECODE is not passed,
+ * all, some, or none of the notifications may fire before the call returns.
+ *
+ * This interface will be cleaned up in bug 505385.
*
* @author Stuart Parmenter <pavlov@netscape.com>
* @version 0.1
* @see imagelib2
*/
-[scriptable, uuid(1dfc9189-6421-4281-83b2-d9c1c9ba4d1b)]
+[scriptable, uuid(9f6bfbee-9e04-43a0-b8f6-2159973efec8)]
interface imgIDecoderObserver : imgIContainerObserver
{
/**
+ * Load notification.
+ *
* called at the same time that nsIRequestObserver::onStartRequest would be
* (used only for observers of imgIRequest objects, which are nsIRequests,
* not imgIDecoder objects)
*
* Unlike nsIRequestObserver::onStartRequest, this can be called
* synchronously.
*/
void onStartRequest(in imgIRequest aRequest);
/**
- * called as soon as the image begins getting decoded
+ * Decode notification.
+ *
+ * Called as soon as the image begins getting decoded. This does not include
+ * "header-only" decodes used by decode-on-draw to parse the width/height
+ * out of the image. Thus, it is a decode notification only.
*/
void onStartDecode(in imgIRequest aRequest);
/**
- * called once the image has been inited and therefore has a width and height
+ * Load notification.
+ *
+ * Called once enough data has been loaded from the network that we were able
+ * to parse the width/height from the image. By the time this callback is been
+ * called, the size has been set on the container and STATUS_SIZE_AVAILABLE
+ * has been set on the associated imgRequest.
*/
void onStartContainer(in imgIRequest aRequest, in imgIContainer aContainer);
/**
- * called when each frame is created
+ * Decode notification.
+ *
+ * called when each frame is created.
*/
void onStartFrame(in imgIRequest aRequest, in unsigned long aFrame);
/**
- * called when some part of the frame has new data in it
+ * Decode notification.
+ *
+ * called when there is more to paint.
*/
[noscript] void onDataAvailable(in imgIRequest aRequest, in boolean aCurrentFrame, [const] in nsIntRect aRect);
/**
- * called when a frame is finished decoding
+ * Decode notification.
+ *
+ * called when a frame is finished decoding.
*/
void onStopFrame(in imgIRequest aRequest, in unsigned long aFrame);
/**
- * probably not needed. called right before onStopDecode
+ * Do not implement this. It is useless and going away.
*/
void onStopContainer(in imgIRequest aRequest, in imgIContainer aContainer);
/**
- * called when the decoder is dying off
+ * In theory a decode notification, but currently a load notification.
+ *
+ * Ideally this would be called when the decode is complete. Unfortunately,
+ * this is currently the only way to signal decoding errors to consumers,
+ * and the only decoding errors that consumers care about (indeed, the only
+ * ones that they're prepared to hear about) are failures to instantiate the
+ * decoder (<img src="foo.html"> for example). Thus, currently this is just
+ * a companion to onStopDecode to signal success or failure. This will be
+ * revisited in bug 505385. If you're thinking of doing something new with
+ * this, please talk to bholley first.
*/
void onStopDecode(in imgIRequest aRequest, in nsresult status,
in wstring statusArg);
/**
+ * Load notification.
+ *
* called at the same time that nsIRequestObserver::onStopRequest would be
* (used only for observers of imgIRequest objects, which are nsIRequests,
* not imgIDecoder objects)
*
* Unlike nsIRequestObserver::onStartRequest, this can be called
* synchronously.
*/
void onStopRequest(in imgIRequest aRequest, in boolean aIsLastPart);
+ /**
+ * Called when the decoded image data is discarded. This means that the frames
+ * no longer exist in decoded form, and any attempt to access or draw the
+ * image will initiate a new series of progressive decode notifications.
+ */
+ void onDiscard(in imgIRequest aRequest);
+
};
deleted file mode 100644
--- a/modules/libpr0n/public/imgILoad.idl
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Stuart Parmenter <pavlov@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-interface imgIContainer;
-
-/**
- * imgILoad interface
- *
- * @author Stuart Parmenter <pavlov@netscape.com>
- * @version 0.1
- * @see imagelib2
- */
-[scriptable, uuid(e6273acc-1dd1-11b2-a08b-824ad1b1628d)]
-interface imgILoad : nsISupports
-{
- /**
- * the image container...
- * @return the image object associated with the request.
- * @attention NEED DOCS
- */
- attribute imgIContainer image;
- readonly attribute PRBool isMultiPartChannel;
-};
--- a/modules/libpr0n/public/imgIRequest.idl
+++ b/modules/libpr0n/public/imgIRequest.idl
@@ -96,18 +96,17 @@ interface imgIRequest : nsIRequest
const long STATUS_SIZE_AVAILABLE = 0x1;
const long STATUS_LOAD_PARTIAL = 0x2;
const long STATUS_LOAD_COMPLETE = 0x4;
const long STATUS_ERROR = 0x8;
const long STATUS_FRAME_COMPLETE = 0x10;
//@}
/**
- * something
- * @attention NEED DOCS
+ * Status flags of the STATUS_* variety.
*/
readonly attribute unsigned long imageStatus;
/**
* The URI the image load was started with. Note that this might not be the
* actual URI for the image (e.g. if HTTP redirects happened during the
* load).
*/
@@ -135,10 +134,46 @@ interface imgIRequest : nsIRequest
* decoderObserver so it gets no further notifications from us.
*
* NOTE: You should not use this in any new code; instead, use cancel(). Note
* that cancel() is asynchronous, which means that some time after you call
* it, the listener/observer will get an OnStopRequest(). This means that, if
* you're the observer, you can't call cancel() from your destructor.
*/
void cancelAndForgetObserver(in nsresult aStatus);
+
+ /**
+ * Requests a decode for the image.
+ *
+ * imgIContainer has a requestDecode() method, but callers may want to request
+ * a decode before the container has necessarily been instantiated. Calling
+ * requestDecode() on the imgIRequest simply forwards along the request if the
+ * container already exists, or calls it once it gets OnStartContainer if the
+ * container does not yet exist.
+ */
+ void requestDecode();
+
+ /**
+ * Locks an image. If the image does not exist yet, locks it once it becomes
+ * available. The lock persists for the lifetime of the imgIRequest (until
+ * unlockImage is called) even if the underlying image changes.
+ *
+ * If you don't call unlockImage() by the time this imgIRequest goes away, it
+ * will be called for you automatically.
+ *
+ * Only one lock at a time per imgIRequest is supported. That is to say, you
+ * can do foo->LockImage(); foo->UnlockImage(); foo->LockImage();
+ * foo->UnlockImage(); but not foo->LockImage(); foo->LockImage();
+ * foo->UnlockImage(); foo->UnlockImage();
+ *
+ * @see imgIContainer::lockImage for documentation of the underlying call.
+ */
+ void lockImage();
+
+ /**
+ * Unlocks an image.
+ *
+ * @see imgIContainer::unlockImage for documentation of the underlying call.
+ */
+ void unlockImage();
+
};
--- a/modules/libpr0n/public/imgITools.idl
+++ b/modules/libpr0n/public/imgITools.idl
@@ -53,17 +53,18 @@ interface imgITools : nsISupports
*
* @param aStream
* An input stream for an encoded image file.
* @param aMimeType
* Type of image in the stream.
* @param aContainer
* An imgIContainer holding the decoded image. Specify |null| when
* calling to have one created, otherwise specify a container to
- * be reused.
+ * be used. It is an error to pass an already-initialized container
+ * as aContainer.
*/
void decodeImageData(in nsIInputStream aStream,
in ACString aMimeType,
inout imgIContainer aContainer);
/**
* encodeImage
* Caller provides an image container, and the mime type it should be
--- a/modules/libpr0n/src/imgContainer.cpp
+++ b/modules/libpr0n/src/imgContainer.cpp
@@ -21,16 +21,17 @@
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
* Chris Saari <saari@netscape.com>
* Asko Tontti <atontti@cc.hut.fi>
* Arron Mogge <paper@animecity.nu>
* Andrew Smith
* Federico Mena-Quintero <federico@novell.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -39,176 +40,349 @@
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsComponentManagerUtils.h"
#include "imgIContainerObserver.h"
#include "ImageErrors.h"
-#include "imgILoad.h"
#include "imgIDecoder.h"
#include "imgIDecoderObserver.h"
#include "imgContainer.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsAutoPtr.h"
#include "nsStringStream.h"
#include "prmem.h"
#include "prlog.h"
#include "prenv.h"
+#include "nsTime.h"
+#include "ImageLogging.h"
#include "gfxContext.h"
/* Accounting for compressed data */
#if defined(PR_LOGGING)
static PRLogModuleInfo *gCompressedImageAccountingLog = PR_NewLogModule ("CompressedImageAccounting");
#else
#define gCompressedImageAccountingLog
#endif
-static int num_containers_with_discardable_data;
-static PRInt64 num_compressed_image_bytes;
-
-
-NS_IMPL_ISUPPORTS3(imgContainer, imgIContainer, nsITimerCallback, nsIProperties)
+/* We define our own error checking macros here for 2 reasons:
+ *
+ * 1) Most of the failures we encounter here will (hopefully) be
+ * the result of decoding failures (ie, bad data) and not code
+ * failures. As such, we don't want to clutter up debug consoles
+ * with spurious messages about NS_ENSURE_SUCCESS failures.
+ *
+ * 2) We want to set the internal error flag, shutdown properly,
+ * and end up in an error state.
+ *
+ * So this macro should be called when the desired failure behavior
+ * is to put the container into an error state and return failure.
+ * It goes without saying that macro won't compile outside of a
+ * non-static imgContainer method.
+ */
+#define LOG_CONTAINER_ERROR \
+ PR_BEGIN_MACRO \
+ PR_LOG (gImgLog, PR_LOG_ERROR, \
+ ("ImgContainer: [this=%p] Error " \
+ "detected at line %u for image of " \
+ "type %s\n", this, __LINE__, \
+ mSourceDataMimeType.get())); \
+ PR_END_MACRO
+
+#define CONTAINER_ENSURE_SUCCESS(status) \
+ PR_BEGIN_MACRO \
+ nsresult _status = status; /* eval once */ \
+ if (_status) { \
+ LOG_CONTAINER_ERROR; \
+ DoError(); \
+ return _status; \
+ } \
+ PR_END_MACRO
+
+#define CONTAINER_ENSURE_TRUE(arg, rv) \
+ PR_BEGIN_MACRO \
+ if (!(arg)) { \
+ LOG_CONTAINER_ERROR; \
+ DoError(); \
+ return rv; \
+ } \
+ PR_END_MACRO
+
+
+
+static int num_containers;
+static int num_discardable_containers;
+static PRInt64 total_source_bytes;
+static PRInt64 discardable_source_bytes;
+
+/* Are we globally disabling image discarding? */
+static PRBool
+DiscardingEnabled()
+{
+ static PRBool inited;
+ static PRBool enabled;
+
+ if (!inited) {
+ inited = PR_TRUE;
+
+ enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nsnull);
+ }
+
+ return enabled;
+}
+
+NS_IMPL_ISUPPORTS4(imgContainer, imgIContainer, nsITimerCallback, nsIProperties,
+ nsISupportsWeakReference)
//******************************************************************************
imgContainer::imgContainer() :
mSize(0,0),
- mNumFrames(0),
+ mHasSize(PR_FALSE),
mAnim(nsnull),
mAnimationMode(kNormalAnimMode),
mLoopCount(-1),
mObserver(nsnull),
+ mDecodeOnDraw(PR_FALSE),
+ mMultipart(PR_FALSE),
+ mInitialized(PR_FALSE),
mDiscardable(PR_FALSE),
- mDiscarded(PR_FALSE),
- mRestoreDataDone(PR_FALSE),
- mDiscardTimer(nsnull)
+ mLockCount(0),
+ mDiscardTimer(nsnull),
+ mHasSourceData(PR_FALSE),
+ mDecoded(PR_FALSE),
+ mDecoder(nsnull),
+ mWorker(nsnull),
+ mBytesDecoded(0),
+ mDecoderInput(nsnull),
+ mDecoderFlags(imgIDecoder::DECODER_FLAG_NONE),
+ mWorkerPending(PR_FALSE),
+ mInDecoder(PR_FALSE),
+ mError(PR_FALSE)
{
+ // Statistics
+ num_containers++;
}
//******************************************************************************
imgContainer::~imgContainer()
{
if (mAnim)
delete mAnim;
for (unsigned int i = 0; i < mFrames.Length(); ++i)
delete mFrames[i];
- if (!mRestoreData.IsEmpty()) {
- num_containers_with_discardable_data--;
- num_compressed_image_bytes -= mRestoreData.Length();
+ // Discardable statistics
+ if (mDiscardable) {
+ num_discardable_containers--;
+ discardable_source_bytes -= mSourceData.Length();
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
("CompressedImageAccounting: destroying imgContainer %p. "
- "Compressed containers: %d, Compressed data bytes: %lld",
+ "Total Containers: %d, Discardable containers: %d, "
+ "Total source bytes: %lld, Source bytes for discardable containers %lld",
this,
- num_containers_with_discardable_data,
- num_compressed_image_bytes));
+ num_containers,
+ num_discardable_containers,
+ total_source_bytes,
+ discardable_source_bytes));
}
if (mDiscardTimer) {
- mDiscardTimer->Cancel ();
+ mDiscardTimer->Cancel();
mDiscardTimer = nsnull;
}
+
+ // If we have a decoder open, shut it down
+ if (mDecoder) {
+ nsresult rv = ShutdownDecoder(eShutdownIntent_Interrupted);
+ if (NS_FAILED(rv))
+ NS_WARNING("Failed to shut down decoder in destructor!");
+ }
+
+ // Total statistics
+ num_containers--;
+ total_source_bytes -= mSourceData.Length();
}
//******************************************************************************
-/* void init (in PRInt32 aWidth, in PRInt32 aHeight,
- in imgIContainerObserver aObserver); */
-NS_IMETHODIMP imgContainer::Init(PRInt32 aWidth, PRInt32 aHeight,
- imgIContainerObserver *aObserver)
+/* void init(in imgIDecoderObserver aObserver, in string aMimeType,
+ in PRUint32 aFlags); */
+NS_IMETHODIMP imgContainer::Init(imgIDecoderObserver *aObserver,
+ const char* aMimeType,
+ PRUint32 aFlags)
{
- if (aWidth <= 0 || aHeight <= 0) {
- NS_WARNING("error - negative image size\n");
+ // We don't support re-initialization
+ if (mInitialized)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ // Not sure an error can happen before init, but be safe
+ if (mError)
return NS_ERROR_FAILURE;
+
+ NS_ENSURE_ARG_POINTER(aMimeType);
+
+ // We must be non-discardable and non-decode-on-draw for
+ // multipart channels
+ NS_ABORT_IF_FALSE(!(aFlags & INIT_FLAG_MULTIPART) ||
+ (!(aFlags & INIT_FLAG_DISCARDABLE) &&
+ !(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
+ "Can't be discardable or decode-on-draw for multipart");
+
+ // Store initialization data
+ mObserver = do_GetWeakReference(aObserver);
+ mSourceDataMimeType.Assign(aMimeType);
+ mDiscardable = aFlags & INIT_FLAG_DISCARDABLE;
+ mDecodeOnDraw = aFlags & INIT_FLAG_DECODE_ON_DRAW;;
+ mMultipart = aFlags & INIT_FLAG_MULTIPART;
+
+ // Statistics
+ if (mDiscardable) {
+ num_discardable_containers++;
+ discardable_source_bytes += mSourceData.Length();
}
- mSize.SizeTo(aWidth, aHeight);
-
- // As we are reloading it means we are no longer in 'discarded' state
- mDiscarded = PR_FALSE;
-
- mObserver = do_GetWeakReference(aObserver);
-
+ // If we're being called from ExtractFrame (used by borderimage),
+ // we don't actually do any decoding. Bail early.
+ // XXX - This should be removed when we fix borderimage
+ if (mSourceDataMimeType.Length() == 0) {
+ mInitialized = PR_TRUE;
+ return NS_OK;
+ }
+
+ // Determine our decoder flags. If we're doing decode-on-draw,
+ // we want to do a quick first pass to get the size but nothing
+ // else. We instantiate another decoder later to do the full
+ // decoding.
+ PRUint32 dFlags = imgIDecoder::DECODER_FLAG_NONE;
+ if (mDecodeOnDraw)
+ dFlags |= imgIDecoder::DECODER_FLAG_HEADERONLY;
+
+ // Instantiate the decoder
+ nsresult rv = InitDecoder(dFlags);
+ CONTAINER_ENSURE_SUCCESS(rv);
+
+ // Mark us as initialized
+ mInitialized = PR_TRUE;
+
return NS_OK;
}
//******************************************************************************
-/* [noscript] imgIContainer extractCurrentFrame([const] in nsIntRect aRegion); */
-NS_IMETHODIMP imgContainer::ExtractCurrentFrame(const nsIntRect &aRegion, imgIContainer **_retval)
+/* [noscript] imgIContainer extractFrame(PRUint32 aWhichFrame,
+ * [const] in nsIntRect aRegion,
+ * in PRUint32 aFlags); */
+NS_IMETHODIMP imgContainer::ExtractFrame(PRUint32 aWhichFrame,
+ const nsIntRect &aRegion,
+ PRUint32 aFlags,
+ imgIContainer **_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
+ nsresult rv;
+
+ if (aWhichFrame > FRAME_MAX_VALUE)
+ return NS_ERROR_INVALID_ARG;
+
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // Make a new container. This should switch to another class with bug 505959.
nsRefPtr<imgContainer> img(new imgContainer());
NS_ENSURE_TRUE(img, NS_ERROR_OUT_OF_MEMORY);
- img->Init(aRegion.width, aRegion.height, nsnull);
-
- imgFrame *frame = GetCurrentImgFrame();
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
+ // We don't actually have a mimetype in this case. The empty string tells the
+ // init routine not to try to instantiate a decoder. This should be fixed in
+ // bug 505959.
+ img->Init(nsnull, "", INIT_FLAG_NONE);
+ img->SetSize(aRegion.width, aRegion.height);
+ img->mDecoded = PR_TRUE; // Also, we need to mark the image as decoded
+
+ // If a synchronous decode was requested, do it
+ if (aFlags & FLAG_SYNC_DECODE) {
+ rv = SyncDecode();
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // Get the frame. If it's not there, it's probably the caller's fault for
+ // not waiting for the data to be loaded from the network or not passing
+ // FLAG_SYNC_DECODE
+ PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
+ 0 : GetCurrentImgFrameIndex();
+ imgFrame *frame = GetImgFrame(frameIndex);
+ if (!frame) {
+ *_retval = nsnull;
+ return NS_ERROR_FAILURE;
+ }
// The frame can be smaller than the image. We want to extract only the part
// of the frame that actually exists.
nsIntRect framerect = frame->GetRect();
framerect.IntersectRect(framerect, aRegion);
if (framerect.IsEmpty())
return NS_ERROR_NOT_AVAILABLE;
nsAutoPtr<imgFrame> subframe;
- nsresult rv = frame->Extract(framerect, getter_Transfers(subframe));
+ rv = frame->Extract(framerect, getter_Transfers(subframe));
if (NS_FAILED(rv))
return rv;
img->mFrames.AppendElement(subframe.forget());
- img->mNumFrames++;
*_retval = img.forget().get();
return NS_OK;
}
//******************************************************************************
/* readonly attribute PRInt32 width; */
NS_IMETHODIMP imgContainer::GetWidth(PRInt32 *aWidth)
{
NS_ENSURE_ARG_POINTER(aWidth);
+ if (mError)
+ return NS_ERROR_FAILURE;
+
*aWidth = mSize.width;
return NS_OK;
}
//******************************************************************************
/* readonly attribute PRInt32 height; */
NS_IMETHODIMP imgContainer::GetHeight(PRInt32 *aHeight)
{
NS_ENSURE_ARG_POINTER(aHeight);
+ if (mError)
+ return NS_ERROR_FAILURE;
+
*aHeight = mSize.height;
return NS_OK;
}
imgFrame *imgContainer::GetImgFrame(PRUint32 framenum)
{
- nsresult rv = RestoreDiscardedData();
- NS_ENSURE_SUCCESS(rv, nsnull);
+ nsresult rv = WantDecodedFrames();
+ CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nsnull);
if (!mAnim) {
NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
return mFrames.SafeElementAt(0, nsnull);
}
if (mAnim->lastCompositedFrameIndex == PRInt32(framenum))
return mAnim->compositingFrame;
return mFrames.SafeElementAt(framenum, nsnull);
}
-PRInt32 imgContainer::GetCurrentImgFrameIndex() const
+PRUint32 imgContainer::GetCurrentImgFrameIndex() const
{
if (mAnim)
return mAnim->currentAnimationFrameIndex;
return 0;
}
imgFrame *imgContainer::GetCurrentImgFrame()
@@ -217,83 +391,142 @@ imgFrame *imgContainer::GetCurrentImgFra
}
//******************************************************************************
/* readonly attribute boolean currentFrameIsOpaque; */
NS_IMETHODIMP imgContainer::GetCurrentFrameIsOpaque(PRBool *aIsOpaque)
{
NS_ENSURE_ARG_POINTER(aIsOpaque);
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // See if we can get an image frame
imgFrame *curframe = GetCurrentImgFrame();
- NS_ENSURE_TRUE(curframe, NS_ERROR_FAILURE);
-
- *aIsOpaque = !curframe->GetNeedsBackground();
-
- // We are also transparent if the current frame's size doesn't cover our
- // entire area.
- nsIntRect framerect = curframe->GetRect();
- *aIsOpaque = *aIsOpaque && (framerect != nsIntRect(0, 0, mSize.width, mSize.height));
+
+ // If we don't get a frame, the safe answer is "not opaque"
+ if (!curframe)
+ *aIsOpaque = PR_FALSE;
+
+ // Otherwise, we can make a more intelligent decision
+ else {
+ *aIsOpaque = !curframe->GetNeedsBackground();
+
+ // We are also transparent if the current frame's size doesn't cover our
+ // entire area.
+ nsIntRect framerect = curframe->GetRect();
+ *aIsOpaque = *aIsOpaque && (framerect != nsIntRect(0, 0, mSize.width, mSize.height));
+ }
return NS_OK;
}
//******************************************************************************
/* [noscript] void getCurrentFrameRect(nsIntRect rect); */
NS_IMETHODIMP imgContainer::GetCurrentFrameRect(nsIntRect &aRect)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // Get the current frame
imgFrame *curframe = GetCurrentImgFrame();
- NS_ENSURE_TRUE(curframe, NS_ERROR_FAILURE);
-
- aRect = curframe->GetRect();
+
+ // If we have the frame, use that rectangle
+ if (curframe)
+ aRect = curframe->GetRect();
+
+ // If the frame doesn't exist, we pass the empty rectangle. It's not clear
+ // whether this is appropriate in general, but at the moment the only
+ // consumer of this method is imgRequest (when it wants to figure out dirty
+ // rectangles to send out batched observer updates). This should probably be
+ // revisited when we fix bug 503973.
+ else {
+ aRect.MoveTo(0, 0);
+ aRect.SizeTo(0, 0);
+ }
return NS_OK;
}
//******************************************************************************
/* readonly attribute unsigned long currentFrameIndex; */
NS_IMETHODIMP imgContainer::GetCurrentFrameIndex(PRUint32 *aCurrentFrameIdx)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(aCurrentFrameIdx);
*aCurrentFrameIdx = GetCurrentImgFrameIndex();
return NS_OK;
}
//******************************************************************************
/* readonly attribute unsigned long numFrames; */
NS_IMETHODIMP imgContainer::GetNumFrames(PRUint32 *aNumFrames)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(aNumFrames);
- *aNumFrames = mNumFrames;
+ *aNumFrames = mFrames.Length();
return NS_OK;
}
//******************************************************************************
/* readonly attribute boolean animated; */
NS_IMETHODIMP imgContainer::GetAnimated(PRBool *aAnimated)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(aAnimated);
- *aAnimated = (mNumFrames > 1);
+ *aAnimated = (mAnim != nsnull);
return NS_OK;
}
//******************************************************************************
-/* [noscript] gfxImageSurface copyCurrentFrame(); */
-NS_IMETHODIMP imgContainer::CopyCurrentFrame(gfxImageSurface **_retval)
+/* [noscript] gfxImageSurface copyFrame(in PRUint32 aWhichFrame,
+ * in PRUint32 aFlags); */
+NS_IMETHODIMP imgContainer::CopyFrame(PRUint32 aWhichFrame,
+ PRUint32 aFlags,
+ gfxImageSurface **_retval)
{
+ if (aWhichFrame > FRAME_MAX_VALUE)
+ return NS_ERROR_INVALID_ARG;
+
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+
+ // If requested, synchronously flush any data we have lying around to the decoder
+ if (aFlags & FLAG_SYNC_DECODE) {
+ rv = SyncDecode();
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
NS_ENSURE_ARG_POINTER(_retval);
- imgFrame *frame = GetImgFrame(GetCurrentImgFrameIndex());
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
+ // Get the frame. If it's not there, it's probably the caller's fault for
+ // not waiting for the data to be loaded from the network or not passing
+ // FLAG_SYNC_DECODE
+ PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
+ 0 : GetCurrentImgFrameIndex();
+ imgFrame *frame = GetImgFrame(frameIndex);
+ if (!frame) {
+ *_retval = nsnull;
+ return NS_ERROR_FAILURE;
+ }
nsRefPtr<gfxPattern> pattern;
frame->GetPattern(getter_AddRefs(pattern));
nsIntRect intframerect = frame->GetRect();
gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height);
// Create a 32-bit image surface of our size, but draw using the frame's
// rect, implicitly padding the frame out to the image's size.
@@ -305,118 +538,129 @@ NS_IMETHODIMP imgContainer::CopyCurrentF
ctx.Rectangle(framerect);
ctx.Fill();
*_retval = imgsurface.forget().get();
return NS_OK;
}
//******************************************************************************
-/* [noscript] readonly attribute gfxASurface currentFrame; */
-NS_IMETHODIMP imgContainer::GetCurrentFrame(gfxASurface **_retval)
+/* [noscript] gfxASurface getFrame(in PRUint32 aWhichFrame,
+ * in PRUint32 aFlags); */
+NS_IMETHODIMP imgContainer::GetFrame(PRUint32 aWhichFrame,
+ PRUint32 aFlags,
+ gfxASurface **_retval)
{
- imgFrame *frame = GetImgFrame(GetCurrentImgFrameIndex());
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
+ if (aWhichFrame > FRAME_MAX_VALUE)
+ return NS_ERROR_INVALID_ARG;
+
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = NS_OK;
+
+ // If the caller requested a synchronous decode, do it
+ if (aFlags & FLAG_SYNC_DECODE) {
+ rv = SyncDecode();
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // Get the frame. If it's not there, it's probably the caller's fault for
+ // not waiting for the data to be loaded from the network or not passing
+ // FLAG_SYNC_DECODE
+ PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
+ 0 : GetCurrentImgFrameIndex();
+ imgFrame *frame = GetImgFrame(frameIndex);
+ if (!frame) {
+ *_retval = nsnull;
+ return NS_ERROR_FAILURE;
+ }
nsRefPtr<gfxASurface> framesurf;
- nsresult rv = NS_OK;
// If this frame covers the entire image, we can just reuse its existing
// surface.
nsIntRect framerect = frame->GetRect();
if (framerect.x == 0 && framerect.y == 0 &&
framerect.width == mSize.width &&
framerect.height == mSize.height)
rv = frame->GetSurface(getter_AddRefs(framesurf));
// The image doesn't have a surface because it's been optimized away. Create
// one.
if (!framesurf) {
nsRefPtr<gfxImageSurface> imgsurf;
- rv = CopyCurrentFrame(getter_AddRefs(imgsurf));
+ rv = CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
framesurf = imgsurf;
}
*_retval = framesurf.forget().get();
return rv;
}
//******************************************************************************
-/* unsigned long getFrameDataLength(in unsigned long framenum); */
-NS_IMETHODIMP imgContainer::GetFrameImageDataLength(PRUint32 framenum, PRUint32 *_retval)
+/* readonly attribute unsigned long dataSize; */
+NS_IMETHODIMP imgContainer::GetDataSize(PRUint32 *_retval)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(_retval);
- if (framenum >= PRUint32(mNumFrames))
- return NS_ERROR_INVALID_ARG;
-
- imgFrame *frame = GetImgFrame(framenum);
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
- *_retval = frame->GetImageDataLength();
-
- return NS_OK;
-}
-
-//******************************************************************************
-/* unsigned long getFrameColormap(unsigned long framenumber,
- * [array, size_is(paletteLength)] out PRUint32 paletteData,
- * out unsigned long paletteLength); */
-NS_IMETHODIMP imgContainer::GetFrameColormap(PRUint32 framenum, PRUint32 **aPaletteData,
- PRUint32 *aPaletteLength)
-{
- NS_ENSURE_ARG_POINTER(aPaletteData);
- NS_ENSURE_ARG_POINTER(aPaletteLength);
-
- if (framenum >= PRUint32(mNumFrames))
- return NS_ERROR_INVALID_ARG;
-
- imgFrame *frame = GetImgFrame(framenum);
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
- if (!frame->GetIsPaletted())
- return NS_ERROR_FAILURE;
-
- frame->GetPaletteData(aPaletteData, aPaletteLength);
+ // Start with 0
+ *_retval = 0;
+
+ // Account for any compressed source data
+ *_retval += mSourceData.Length();
+ NS_ABORT_IF_FALSE(StoringSourceData() || (*_retval == 0),
+ "Non-zero source data size when we aren't storing it?");
+
+ // Account for any uncompressed frames
+ for (PRUint32 i = 0; i < mFrames.Length(); ++i) {
+ imgFrame *frame = mFrames.SafeElementAt(i, nsnull);
+ NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
+ *_retval += frame->GetImageDataLength();
+ }
return NS_OK;
}
nsresult imgContainer::InternalAddFrameHelper(PRUint32 framenum, imgFrame *aFrame,
PRUint8 **imageData, PRUint32 *imageLength,
PRUint32 **paletteData, PRUint32 *paletteLength)
{
- if (framenum > PRUint32(mNumFrames))
+ NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
+ if (framenum > mFrames.Length())
return NS_ERROR_INVALID_ARG;
nsAutoPtr<imgFrame> frame(aFrame);
if (paletteData && paletteLength)
frame->GetPaletteData(paletteData, paletteLength);
frame->GetImageData(imageData, imageLength);
mFrames.InsertElementAt(framenum, frame.forget());
- mNumFrames++;
return NS_OK;
}
nsresult imgContainer::InternalAddFrame(PRUint32 framenum,
PRInt32 aX, PRInt32 aY,
PRInt32 aWidth, PRInt32 aHeight,
gfxASurface::gfxImageFormat aFormat,
PRUint8 aPaletteDepth,
PRUint8 **imageData,
PRUint32 *imageLength,
PRUint32 **paletteData,
PRUint32 *paletteLength)
{
- if (framenum > PRUint32(mNumFrames))
+ NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
+ if (framenum > mFrames.Length())
return NS_ERROR_INVALID_ARG;
nsAutoPtr<imgFrame> frame(new imgFrame());
NS_ENSURE_TRUE(frame, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
NS_ENSURE_SUCCESS(rv, rv);
@@ -460,58 +704,105 @@ nsresult imgContainer::InternalAddFrame(
/* [noscript] void appendFrame (in PRInt32 aX, in PRInt32 aY, in PRInt32 aWidth, in PRInt32 aHeight, in gfxImageFormat aFormat, [array, size_is (imageLength)] out PRUint8 imageData, out unsigned long imageLength); */
NS_IMETHODIMP imgContainer::AppendFrame(PRInt32 aX, PRInt32 aY, PRInt32 aWidth,
PRInt32 aHeight,
gfxASurface::gfxImageFormat aFormat,
PRUint8 **imageData,
PRUint32 *imageLength)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(imageData);
NS_ENSURE_ARG_POINTER(imageLength);
- return InternalAddFrame(mNumFrames, aX, aY, aWidth, aHeight, aFormat,
+ return InternalAddFrame(mFrames.Length(), aX, aY, aWidth, aHeight, aFormat,
/* aPaletteDepth = */ 0, imageData, imageLength,
/* aPaletteData = */ nsnull,
/* aPaletteLength = */ nsnull);
}
/* [noscript] void appendPalettedFrame (in PRInt32 aX, in PRInt32 aY, in PRInt32 aWidth, in PRInt32 aHeight, in gfxImageFormat aFormat, in PRUint8 aPaletteDepth, [array, size_is (imageLength)] out PRUint8 imageData, out unsigned long imageLength, [array, size_is (paletteLength)] out PRUint32 paletteData, out unsigned long paletteLength); */
NS_IMETHODIMP imgContainer::AppendPalettedFrame(PRInt32 aX, PRInt32 aY,
PRInt32 aWidth, PRInt32 aHeight,
gfxASurface::gfxImageFormat aFormat,
PRUint8 aPaletteDepth,
PRUint8 **imageData,
PRUint32 *imageLength,
PRUint32 **paletteData,
PRUint32 *paletteLength)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(imageData);
NS_ENSURE_ARG_POINTER(imageLength);
NS_ENSURE_ARG_POINTER(paletteData);
NS_ENSURE_ARG_POINTER(paletteLength);
- return InternalAddFrame(mNumFrames, aX, aY, aWidth, aHeight, aFormat,
+ return InternalAddFrame(mFrames.Length(), aX, aY, aWidth, aHeight, aFormat,
aPaletteDepth, imageData, imageLength,
paletteData, paletteLength);
}
-/* [noscript] void ensureCleanFrame(in unsigned long aFramenum, in PRInt32 aX, in PRInt32 aY, in PRInt32 aWidth, in PRInt32 aHeight, in gfxImageFormat aFormat, [array, size_is(imageLength)] out PRUint8 imageData, out unsigned long imageLength); */
+/* [noscript] void setSize(in long aWidth, in long aHeight); */
+NS_IMETHODIMP imgContainer::SetSize(PRInt32 aWidth, PRInt32 aHeight)
+{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // Ensure that we have positive values
+ // XXX - Why isn't the size unsigned? Should this be changed?
+ if ((aWidth < 0) || (aHeight < 0))
+ return NS_ERROR_INVALID_ARG;
+
+ // if we already have a size, check the new size against the old one
+ if (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");
+
+ DoError();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Set the size and flag that we have it
+ mSize.SizeTo(aWidth, aHeight);
+ mHasSize = PR_TRUE;
+
+ return NS_OK;
+}
+
+/* [noscript] void ensureCleanFrame(in unsigned long aFramenum, in PRInt32 aX,
+ in PRInt32 aY, in PRInt32 aWidth,
+ in PRInt32 aHeight, in gfxImageFormat aFormat,
+ [array, size_is(imageLength)]
+ out PRUint8 imageData,
+ out unsigned long imageLength); */
NS_IMETHODIMP imgContainer::EnsureCleanFrame(PRUint32 aFrameNum, PRInt32 aX, PRInt32 aY,
PRInt32 aWidth, PRInt32 aHeight,
gfxASurface::gfxImageFormat aFormat,
PRUint8 **imageData, PRUint32 *imageLength)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(imageData);
NS_ENSURE_ARG_POINTER(imageLength);
- if (aFrameNum > PRUint32(mNumFrames))
+ NS_ABORT_IF_FALSE(aFrameNum <= mFrames.Length(), "Invalid frame index!");
+ if (aFrameNum > mFrames.Length())
return NS_ERROR_INVALID_ARG;
// Adding a frame that doesn't already exist.
- if (aFrameNum == PRUint32(mNumFrames))
+ if (aFrameNum == mFrames.Length())
return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
/* aPaletteDepth = */ 0, imageData, imageLength,
/* aPaletteData = */ nsnull,
/* aPaletteLength = */ nsnull);
imgFrame *frame = GetImgFrame(aFrameNum);
if (!frame)
return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
@@ -536,161 +827,218 @@ NS_IMETHODIMP imgContainer::EnsureCleanF
return NS_OK;
}
//******************************************************************************
/* void frameUpdated (in unsigned long framenumber, in nsIntRect rect); */
NS_IMETHODIMP imgContainer::FrameUpdated(PRUint32 aFrameNum, nsIntRect &aUpdatedRect)
{
- if (aFrameNum >= PRUint32(mNumFrames))
+ NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
+ if (aFrameNum >= mFrames.Length())
return NS_ERROR_INVALID_ARG;
imgFrame *frame = GetImgFrame(aFrameNum);
+ NS_ABORT_IF_FALSE(frame, "Calling FrameUpdated on frame that doesn't exist!");
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
frame->ImageUpdated(aUpdatedRect);
return NS_OK;
}
//******************************************************************************
/* void setFrameDisposalMethod (in unsigned long framenumber, in PRInt32 aDisposalMethod); */
NS_IMETHODIMP imgContainer::SetFrameDisposalMethod(PRUint32 aFrameNum, PRInt32 aDisposalMethod)
{
- if (aFrameNum >= PRUint32(mNumFrames))
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
+ if (aFrameNum >= mFrames.Length())
return NS_ERROR_INVALID_ARG;
imgFrame *frame = GetImgFrame(aFrameNum);
+ NS_ABORT_IF_FALSE(frame,
+ "Calling SetFrameDisposalMethod on frame that doesn't exist!");
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
frame->SetFrameDisposalMethod(aDisposalMethod);
return NS_OK;
}
//******************************************************************************
/* void setFrameTimeout (in unsigned long framenumber, in PRInt32 aTimeout); */
NS_IMETHODIMP imgContainer::SetFrameTimeout(PRUint32 aFrameNum, PRInt32 aTimeout)
{
- if (aFrameNum >= PRUint32(mNumFrames))
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
+ if (aFrameNum >= mFrames.Length())
return NS_ERROR_INVALID_ARG;
imgFrame *frame = GetImgFrame(aFrameNum);
+ NS_ABORT_IF_FALSE(frame, "Calling SetFrameTimeout on frame that doesn't exist!");
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
frame->SetTimeout(aTimeout);
return NS_OK;
}
//******************************************************************************
/* void setFrameBlendMethod (in unsigned long framenumber, in PRInt32 aBlendMethod); */
NS_IMETHODIMP imgContainer::SetFrameBlendMethod(PRUint32 aFrameNum, PRInt32 aBlendMethod)
{
- if (aFrameNum >= PRUint32(mNumFrames))
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
+ if (aFrameNum >= mFrames.Length())
return NS_ERROR_INVALID_ARG;
imgFrame *frame = GetImgFrame(aFrameNum);
+ NS_ABORT_IF_FALSE(frame, "Calling SetFrameBlendMethod on frame that doesn't exist!");
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
frame->SetBlendMethod(aBlendMethod);
return NS_OK;
}
//******************************************************************************
/* void setFrameHasNoAlpha (in unsigned long framenumber); */
NS_IMETHODIMP imgContainer::SetFrameHasNoAlpha(PRUint32 aFrameNum)
{
- if (aFrameNum >= PRUint32(mNumFrames))
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
+ if (aFrameNum >= mFrames.Length())
return NS_ERROR_INVALID_ARG;
imgFrame *frame = GetImgFrame(aFrameNum);
+ NS_ABORT_IF_FALSE(frame, "Calling SetFrameHasNoAlpha on frame that doesn't exist!");
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
frame->SetHasNoAlpha();
return NS_OK;
}
//******************************************************************************
/* void endFrameDecode (in unsigned long framenumber); */
NS_IMETHODIMP imgContainer::EndFrameDecode(PRUint32 aFrameNum)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
// Assume there's another frame.
// currentDecodingFrameIndex is 0 based, aFrameNum is 1 based
if (mAnim)
mAnim->currentDecodingFrameIndex = aFrameNum;
return NS_OK;
}
//******************************************************************************
/* void decodingComplete (); */
NS_IMETHODIMP imgContainer::DecodingComplete(void)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // Flag that we're done decoding.
+ // XXX - these should probably be combined when we fix animated image
+ // discarding with bug 500402.
+ mDecoded = PR_TRUE;
if (mAnim)
mAnim->doneDecoding = PR_TRUE;
- // If there's only 1 frame, optimize it.
- // Optimizing animated images is not supported.
- if (mNumFrames == 1)
- return mFrames[0]->Optimize();
+ nsresult rv;
+
+ // We now have one of the qualifications for discarding. Re-evaluate.
+ if (CanDiscard()) {
+ NS_ABORT_IF_FALSE(!mDiscardTimer,
+ "We shouldn't have been discardable before this");
+ rv = ResetDiscardTimer();
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // If there's only 1 frame, optimize it. Optimizing animated images
+ // is not supported.
+ //
+ // We don't optimize the frame for multipart images because we reuse
+ // the frame.
+ if ((mFrames.Length() == 1) && !mMultipart) {
+ rv = mFrames[0]->Optimize();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
return NS_OK;
}
//******************************************************************************
/* attribute unsigned short animationMode; */
NS_IMETHODIMP imgContainer::GetAnimationMode(PRUint16 *aAnimationMode)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(aAnimationMode);
*aAnimationMode = mAnimationMode;
return NS_OK;
}
//******************************************************************************
/* attribute unsigned short animationMode; */
NS_IMETHODIMP imgContainer::SetAnimationMode(PRUint16 aAnimationMode)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ASSERTION(aAnimationMode == imgIContainer::kNormalAnimMode ||
aAnimationMode == imgIContainer::kDontAnimMode ||
aAnimationMode == imgIContainer::kLoopOnceAnimMode,
"Wrong Animation Mode is being set!");
switch (mAnimationMode = aAnimationMode) {
case kDontAnimMode:
StopAnimation();
break;
case kNormalAnimMode:
if (mLoopCount != 0 ||
- (mAnim && (mAnim->currentAnimationFrameIndex + 1 < mNumFrames)))
+ (mAnim && (mAnim->currentAnimationFrameIndex + 1 < mFrames.Length())))
StartAnimation();
break;
case kLoopOnceAnimMode:
- if (mAnim && (mAnim->currentAnimationFrameIndex + 1 < mNumFrames))
+ if (mAnim && (mAnim->currentAnimationFrameIndex + 1 < mFrames.Length()))
StartAnimation();
break;
}
return NS_OK;
}
//******************************************************************************
/* void startAnimation () */
NS_IMETHODIMP imgContainer::StartAnimation()
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
if (mAnimationMode == kDontAnimMode ||
(mAnim && (mAnim->timer || mAnim->animating)))
return NS_OK;
- if (mNumFrames > 1) {
+ if (mFrames.Length() > 1) {
if (!ensureAnimExists())
return NS_ERROR_OUT_OF_MEMORY;
// Default timeout to 100: the timer notify code will do the right
// thing, so just get that started.
PRInt32 timeout = 100;
imgFrame *currentFrame = GetCurrentImgFrame();
if (currentFrame) {
@@ -710,16 +1058,19 @@ NS_IMETHODIMP imgContainer::StartAnimati
return NS_OK;
}
//******************************************************************************
/* void stopAnimation (); */
NS_IMETHODIMP imgContainer::StopAnimation()
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
if (mAnim) {
mAnim->animating = PR_FALSE;
if (!mAnim->timer)
return NS_OK;
mAnim->timer->Cancel();
mAnim->timer = nsnull;
@@ -727,140 +1078,134 @@ NS_IMETHODIMP imgContainer::StopAnimatio
return NS_OK;
}
//******************************************************************************
/* void resetAnimation (); */
NS_IMETHODIMP imgContainer::ResetAnimation()
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
if (mAnimationMode == kDontAnimMode ||
!mAnim || mAnim->currentAnimationFrameIndex == 0)
return NS_OK;
PRBool oldAnimating = mAnim->animating;
if (mAnim->animating) {
nsresult rv = StopAnimation();
NS_ENSURE_SUCCESS(rv, rv);
}
mAnim->lastCompositedFrameIndex = -1;
mAnim->currentAnimationFrameIndex = 0;
- // Update display
+
+ // Note - We probably want to kick off a redecode somewhere around here when
+ // we fix bug 500402.
+
+ // Update display if we were animating before
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
- if (observer) {
- nsresult rv = RestoreDiscardedData();
- NS_ENSURE_SUCCESS(rv, rv);
+ if (oldAnimating && observer)
observer->FrameChanged(this, &(mAnim->firstFrameRefreshArea));
- }
if (oldAnimating)
return StartAnimation();
return NS_OK;
}
//******************************************************************************
/* attribute long loopCount; */
NS_IMETHODIMP imgContainer::GetLoopCount(PRInt32 *aLoopCount)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(aLoopCount);
*aLoopCount = mLoopCount;
return NS_OK;
}
//******************************************************************************
/* attribute long loopCount; */
NS_IMETHODIMP imgContainer::SetLoopCount(PRInt32 aLoopCount)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
// -1 infinite
// 0 no looping, one iteration
// 1 one loop, two iterations
// ...
mLoopCount = aLoopCount;
return NS_OK;
}
-static PRBool
-DiscardingEnabled(void)
+//******************************************************************************
+/* void addSourceData(in nsIInputStream aInputStream, in unsigned long aCount); */
+NS_IMETHODIMP imgContainer::AddSourceData(const char *aBuffer, PRUint32 aCount)
{
- static PRBool inited;
- static PRBool enabled;
-
- if (!inited) {
- inited = PR_TRUE;
-
- enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nsnull);
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ NS_ENSURE_ARG_POINTER(aBuffer);
+ nsresult rv = NS_OK;
+
+ // We should not call this if we're not initialized
+ NS_ABORT_IF_FALSE(mInitialized, "Calling AddSourceData() on uninitialized "
+ "imgContainer!");
+
+ // We should not call this if we're already finished adding source data
+ NS_ABORT_IF_FALSE(!mHasSourceData, "Calling AddSourceData() after calling "
+ "sourceDataComplete()!");
+
+ // This call should come straight from necko - no reentrancy allowed
+ NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
+
+ // If we're not storing source data, write it directly to the decoder
+ if (!StoringSourceData()) {
+ rv = WriteToDecoder(aBuffer, aCount);
+ CONTAINER_ENSURE_SUCCESS(rv);
}
- return enabled;
-}
-
-//******************************************************************************
-/* void setDiscardable(in string mime_type); */
-NS_IMETHODIMP imgContainer::SetDiscardable(const char* aMimeType)
-{
- NS_ENSURE_ARG_POINTER(aMimeType);
-
- if (!DiscardingEnabled())
- return NS_OK;
-
- if (mDiscardable) {
- NS_WARNING ("imgContainer::SetDiscardable(): cannot change an imgContainer which is already discardable");
- return NS_ERROR_FAILURE;
+ // Otherwise, we're storing data in the source buffer
+ else {
+
+ // Store the data
+ char *newElem = mSourceData.AppendElements(aBuffer, aCount);
+ if (!newElem)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // If there's a decoder open, that means we want to do more decoding.
+ // Wake up the worker if it's not up already
+ if (mDecoder && !mWorkerPending) {
+ NS_ABORT_IF_FALSE(mWorker, "We should have a worker here!");
+ rv = mWorker->Run();
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
}
- mDiscardableMimeType.Assign(aMimeType);
- mDiscardable = PR_TRUE;
-
- num_containers_with_discardable_data++;
+ // Statistics
+ total_source_bytes += aCount;
+ if (mDiscardable)
+ discardable_source_bytes += aCount;
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
- ("CompressedImageAccounting: Making imgContainer %p (%s) discardable. "
- "Compressed containers: %d, Compressed data bytes: %lld",
+ ("CompressedImageAccounting: Added compressed data to imgContainer %p (%s). "
+ "Total Containers: %d, Discardable containers: %d, "
+ "Total source bytes: %lld, Source bytes for discardable containers %lld",
this,
- aMimeType,
- num_containers_with_discardable_data,
- num_compressed_image_bytes));
-
- return NS_OK;
-}
-
-//******************************************************************************
-/* void addRestoreData(in nsIInputStream aInputStream, in unsigned long aCount); */
-NS_IMETHODIMP imgContainer::AddRestoreData(const char *aBuffer, PRUint32 aCount)
-{
- NS_ENSURE_ARG_POINTER(aBuffer);
-
- if (!mDiscardable)
- return NS_OK;
-
- if (mRestoreDataDone) {
- /* We are being called from the decoder while the data is being restored
- * (i.e. we were fully loaded once, then we discarded the image data, then
- * we are being restored). We don't want to save the compressed data again,
- * since we already have it.
- */
- return NS_OK;
- }
-
- if (!mRestoreData.AppendElements(aBuffer, aCount))
- return NS_ERROR_OUT_OF_MEMORY;
-
- num_compressed_image_bytes += aCount;
-
- PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
- ("CompressedImageAccounting: Added compressed data to imgContainer %p (%s). "
- "Compressed containers: %d, Compressed data bytes: %lld",
- this,
- mDiscardableMimeType.get(),
- num_containers_with_discardable_data,
- num_compressed_image_bytes));
+ mSourceDataMimeType.get(),
+ num_containers,
+ num_discardable_containers,
+ total_source_bytes,
+ discardable_source_bytes));
return NS_OK;
}
/* Note! buf must be declared as char buf[9]; */
// just used for logging and hashing the header
static void
get_header_str (char *buf, char *data, PRSize data_len)
@@ -875,87 +1220,156 @@ get_header_str (char *buf, char *data, P
buf[i * 2] = hex[(data[i] >> 4) & 0x0f];
buf[i * 2 + 1] = hex[data[i] & 0x0f];
}
buf[i * 2] = 0;
}
//******************************************************************************
-/* void restoreDataDone(); */
-NS_IMETHODIMP imgContainer::RestoreDataDone (void)
+/* void sourceDataComplete(); */
+NS_IMETHODIMP imgContainer::SourceDataComplete()
{
- // If image is not discardable, don't start discard timer
- if (!mDiscardable)
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // If we've been called before, ignore. Otherwise, flag that we have everything
+ if (mHasSourceData)
return NS_OK;
-
- if (mRestoreDataDone)
- return NS_OK;
-
- mRestoreData.Compact();
-
- mRestoreDataDone = PR_TRUE;
-
+ mHasSourceData = PR_TRUE;
+
+ // This call should come straight from necko - no reentrancy allowed
+ NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
+
+ // If we're not storing any source data, then all the data was written
+ // directly to the decoder in the AddSourceData() calls. This means we're
+ // done, so we can shut down the decoder.
+ if (!StoringSourceData()) {
+ nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // If there's a decoder open, we need to wake up the worker if it's not
+ // already. This is so the worker can account for the fact that the source
+ // data is complete. For some decoders, DecodingComplete() is only called
+ // when the decoder is Close()-ed, and thus the SourceDataComplete() call
+ // is the only way we can transition to a 'decoded' state. Furthermore,
+ // it's always possible for any image type to have the data stream stop
+ // abruptly at any point, in which case we need to trigger an error.
+ if (mDecoder && !mWorkerPending) {
+ NS_ABORT_IF_FALSE(mWorker, "We should have a worker here!");
+ nsresult rv = mWorker->Run();
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // Free up any extra space in the backing buffer
+ mSourceData.Compact();
+
+ // Log header information
if (PR_LOG_TEST(gCompressedImageAccountingLog, PR_LOG_DEBUG)) {
char buf[9];
- get_header_str(buf, mRestoreData.Elements(), mRestoreData.Length());
+ get_header_str(buf, mSourceData.Elements(), mSourceData.Length());
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
- ("CompressedImageAccounting: imgContainer::RestoreDataDone() - data is done for container %p (%s), %d real frames (cached as %d frames) - header %p is 0x%s (length %d)",
+ ("CompressedImageAccounting: imgContainer::SourceDataComplete() - data "
+ "is done for container %p (%s) - header %p is 0x%s (length %d)",
this,
- mDiscardableMimeType.get(),
- mFrames.Length (),
- mNumFrames,
- mRestoreData.Elements(),
+ mSourceDataMimeType.get(),
+ mSourceData.Elements(),
buf,
- mRestoreData.Length()));
+ mSourceData.Length()));
+ }
+
+ // We now have one of the qualifications for discarding. Re-evaluate.
+ if (CanDiscard()) {
+ nsresult rv = ResetDiscardTimer();
+ CONTAINER_ENSURE_SUCCESS(rv);
}
-
- return ResetDiscardTimer();
+ return NS_OK;
+}
+
+//******************************************************************************
+/* void newSourceData(); */
+NS_IMETHODIMP imgContainer::NewSourceData()
+{
+ nsresult rv;
+
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // The source data should be complete before calling this
+ NS_ABORT_IF_FALSE(mHasSourceData,
+ "Calling NewSourceData before SourceDataComplete!");
+ if (!mHasSourceData)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ // Only supported for multipart channels. It wouldn't be too hard to change this,
+ // but it would involve making sure that things worked for decode-on-draw and
+ // discarding. Presently there's no need for this, so we don't.
+ NS_ABORT_IF_FALSE(mMultipart, "NewSourceData not supported for multipart");
+ if (!mMultipart)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ // We're multipart, so we shouldn't be storing source data
+ NS_ABORT_IF_FALSE(!StoringSourceData(),
+ "Shouldn't be storing source data for multipart");
+
+ // We're not storing the source data and we got SourceDataComplete. We should
+ // have shut down the previous decoder
+ NS_ABORT_IF_FALSE(!mDecoder, "Shouldn't have a decoder in NewSourceData");
+
+ // The decoder was shut down and we didn't flag an error, so we should be decoded
+ NS_ABORT_IF_FALSE(mDecoded, "Should be decoded in NewSourceData");
+
+ // Reset some flags
+ mDecoded = PR_FALSE;
+ mHasSourceData = PR_FALSE;
+
+ // 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(imgIDecoder::DECODER_FLAG_NONE);
+ CONTAINER_ENSURE_SUCCESS(rv);
+
+ return NS_OK;
}
//******************************************************************************
/* void notify(in nsITimer timer); */
NS_IMETHODIMP imgContainer::Notify(nsITimer *timer)
{
- // Note that as long as the image is animated, it will not be discarded,
- // so this should never happen...
- nsresult rv = RestoreDiscardedData();
- NS_ENSURE_SUCCESS(rv, rv);
-
// This should never happen since the timer is only set up in StartAnimation()
// after mAnim is checked to exist.
NS_ENSURE_TRUE(mAnim, NS_ERROR_UNEXPECTED);
NS_ASSERTION(mAnim->timer == timer,
"imgContainer::Notify() called with incorrect timer");
if (!mAnim->animating || !mAnim->timer)
return NS_OK;
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
if (!observer) {
// the imgRequest that owns us is dead, we should die now too.
StopAnimation();
return NS_OK;
}
- if (mNumFrames == 0)
+ if (mFrames.Length() == 0)
return NS_OK;
imgFrame *nextFrame = nsnull;
PRInt32 previousFrameIndex = mAnim->currentAnimationFrameIndex;
- PRInt32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
+ PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
PRInt32 timeout = 0;
// If we're done decoding the next frame, go ahead and display it now and
// reinit the timer with the next frame's delay time.
// currentDecodingFrameIndex is not set until the second frame has
// finished decoding (see EndFrameDecode)
if (mAnim->doneDecoding ||
(nextFrameIndex < mAnim->currentDecodingFrameIndex)) {
- if (mNumFrames == nextFrameIndex) {
+ if (mFrames.Length() == nextFrameIndex) {
// End of Animation
// If animation mode is "loop once", it's time to stop animating
if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
StopAnimation();
return NS_OK;
} else {
// We may have used compositingFrame to build a frame, and then copied
@@ -1516,323 +1930,670 @@ get_discard_timer_ms (void)
{
/* FIXME: don't hardcode this */
return 15000; /* 15 seconds */
}
void
imgContainer::sDiscardTimerCallback(nsITimer *aTimer, void *aClosure)
{
+ // Retrieve self pointer and null out the expired timer
imgContainer *self = (imgContainer *) aClosure;
-
- NS_ASSERTION(aTimer == self->mDiscardTimer,
- "imgContainer::DiscardTimerCallback() got a callback for an unknown timer");
-
+ NS_ABORT_IF_FALSE(aTimer == self->mDiscardTimer,
+ "imgContainer::DiscardTimerCallback() got a callback "
+ "for an unknown timer");
self->mDiscardTimer = nsnull;
+ // We should be ok for discard
+ NS_ABORT_IF_FALSE(self->CanDiscard(), "Hit discard callback but can't discard!");
+
+ // We should never discard when we have an active decoder
+ NS_ABORT_IF_FALSE(!self->mDecoder, "Discard callback fired with open decoder!");
+
+ // As soon as an image becomes animated, it becomes non-discardable and any
+ // timers are cancelled.
+ NS_ABORT_IF_FALSE(!self->mAnim, "Discard callback fired for animated image!");
+
+ // For post-operation logging
int old_frame_count = self->mFrames.Length();
- // Don't discard animated images, because we don't handle that very well. (See bug 414259.)
- if (self->mAnim) {
- return;
- }
-
+ // Delete all the decoded frames, then clear the array.
for (int i = 0; i < old_frame_count; ++i)
delete self->mFrames[i];
self->mFrames.Clear();
- self->mDiscarded = PR_TRUE;
-
+ // Flag that we no longer have decoded frames for this image
+ self->mDecoded = PR_FALSE;
+
+ // Notify that we discarded
+ nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(self->mObserver));
+ if (observer)
+ observer->OnDiscard(nsnull);
+
+ // Log
PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG,
- ("CompressedImageAccounting: discarded uncompressed image data from imgContainer %p (%s) - %d frames (cached count: %d); "
- "Compressed containers: %d, Compressed data bytes: %lld",
+ ("CompressedImageAccounting: discarded uncompressed image "
+ "data from imgContainer %p (%s) - %d frames (cached count: %d); "
+ "Total Containers: %d, Discardable containers: %d, "
+ "Total source bytes: %lld, Source bytes for discardable containers %lld",
self,
- self->mDiscardableMimeType.get(),
+ self->mSourceDataMimeType.get(),
old_frame_count,
- self->mNumFrames,
- num_containers_with_discardable_data,
- num_compressed_image_bytes));
+ self->mFrames.Length(),
+ num_containers,
+ num_discardable_containers,
+ total_source_bytes,
+ discardable_source_bytes));
}
nsresult
-imgContainer::ResetDiscardTimer (void)
+imgContainer::ResetDiscardTimer()
{
- if (!mRestoreDataDone)
- return NS_OK;
-
+ // We should not call this function if we can't discard
+ NS_ABORT_IF_FALSE(CanDiscard(), "Calling ResetDiscardTimer but can't discard!");
+
+ // As soon as an image becomes animated it is set non-discardable
+ NS_ABORT_IF_FALSE(!mAnim, "Trying to reset discard timer on animated image!");
+
+ // If we have a timer already ticking, cancel it
if (mDiscardTimer) {
- /* Cancel current timer */
nsresult rv = mDiscardTimer->Cancel();
- NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+ CONTAINER_ENSURE_SUCCESS(rv);
mDiscardTimer = nsnull;
}
- /* Don't activate timer when we are animating... */
- if (mAnim && mAnim->animating)
- return NS_OK;
-
- if (!mDiscardTimer) {
- mDiscardTimer = do_CreateInstance("@mozilla.org/timer;1");
- NS_ENSURE_TRUE(mDiscardTimer, NS_ERROR_OUT_OF_MEMORY);
- }
-
+ // Create a new timer
+ mDiscardTimer = do_CreateInstance("@mozilla.org/timer;1");
+ CONTAINER_ENSURE_TRUE(mDiscardTimer, NS_ERROR_OUT_OF_MEMORY);
+
+ // Activate the timer
return mDiscardTimer->InitWithFuncCallback(sDiscardTimerCallback,
(void *) this,
get_discard_timer_ms (),
nsITimer::TYPE_ONE_SHOT);
}
+// Helper method to determine if we can discard an image
+PRBool
+imgContainer::CanDiscard() {
+ return (DiscardingEnabled() && // Globally enabled...
+ mDiscardable && // ...Enabled at creation time...
+ (mLockCount == 0) && // ...not temporarily disabled...
+ mHasSourceData && // ...have the source data...
+ mDecoded); // ...and have something to discard.
+}
+
+// Helper method to determine if we're storing the source data in a buffer
+// or just writing it directly to the decoder
+PRBool
+imgContainer::StoringSourceData() {
+ return (mDecodeOnDraw || mDiscardable);
+}
+
+
+// Sets up a decoder for this image. It is an error to call this function
+// when decoding is already in process (ie - when mDecoder is non-null).
nsresult
-imgContainer::RestoreDiscardedData(void)
+imgContainer::InitDecoder (PRUint32 dFlags)
+{
+ // Ensure that the decoder is not already initialized
+ NS_ABORT_IF_FALSE(!mDecoder, "Calling InitDecoder() while already decoding!");
+
+ // We shouldn't be firing up a decoder if we already have the frames decoded
+ NS_ABORT_IF_FALSE(!mDecoded, "Calling InitDecoder() but already decoded!");
+
+ // Since we're not decoded, we should not have a discard timer active
+ NS_ABORT_IF_FALSE(!mDiscardTimer, "Discard Timer active in InitDecoder()!");
+
+ // Find and instantiate the decoder
+ nsCAutoString decoderCID(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;3?type=") +
+ mSourceDataMimeType);
+ mDecoder = do_CreateInstance(decoderCID.get());
+ CONTAINER_ENSURE_TRUE(mDecoder, NS_IMAGELIB_ERROR_NO_DECODER);
+
+ // Store the flags for this decoder
+ mDecoderFlags = dFlags;
+
+ // Initialize the decoder
+ nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
+ nsresult result = mDecoder->Init(this, observer, dFlags);
+ CONTAINER_ENSURE_SUCCESS(result);
+
+ // Create an nsIInputStream for the data. Because nsIStringInputStreams don't
+ // like their dependent data to grow dynamically, we reset the stream to the
+ // proper buffer each time we write data to the decoder. Nevertheless, it's
+ // worth keeping the structure around to avoid needless construction and
+ // destruction.
+ mDecoderInput = do_CreateInstance("@mozilla.org/io/string-input-stream;1");
+ CONTAINER_ENSURE_TRUE(mDecoderInput, NS_ERROR_OUT_OF_MEMORY);
+
+ // Create a decode worker
+ mWorker = new imgDecodeWorker(this);
+ CONTAINER_ENSURE_TRUE(mWorker, NS_ERROR_OUT_OF_MEMORY);
+
+ return NS_OK;
+}
+
+// Flushes, closes, and nulls-out a decoder. Cleans up any related decoding
+// state. It is an error to call this function when there is no initialized
+// decoder.
+//
+// aIntent specifies the intent of the shutdown. If aIntent is
+// eShutdownIntent_Done, an error is flagged if we didn't get what we should
+// have out of the decode. If aIntent is eShutdownIntent_Interrupted, we don't
+// check this. If aIntent is eShutdownIntent_Error, we shut down in error mode.
+nsresult
+imgContainer::ShutdownDecoder(eShutdownIntent aIntent)
{
- // mRestoreDataDone = PR_TRUE means that we want to timeout and then discard the image frames
- // So, we only need to restore, if mRestoreDataDone is true, and then only when the frames are discarded...
- if (!mRestoreDataDone)
- return NS_OK;
-
- // Reset timer, as the frames are accessed
- nsresult rv = ResetDiscardTimer();
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (!mDiscarded)
+ // Ensure that our intent is valid
+ NS_ABORT_IF_FALSE((aIntent >= 0) || (aIntent < eShutdownIntent_AllCount),
+ "Invalid shutdown intent");
+
+ // Ensure that the decoder is initialized
+ NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
+
+ nsresult rv;
+
+ // If we're "done" _and_ it's a full decode, flush
+ if ((aIntent == eShutdownIntent_Done) &&
+ !(mDecoderFlags && imgIDecoder::DECODER_FLAG_HEADERONLY)) {
+ mInDecoder = PR_TRUE;
+ rv = mDecoder->Flush();
+ mInDecoder = PR_FALSE;
+
+ // The error case here is a bit tricky. We flag an error, which takes us
+ // back into this function, and then we return.
+ if (NS_FAILED(rv)) {
+ DoError();
+ return rv;
+ }
+ }
+
+ // Close the decoder with the appropriate flags
+ mInDecoder = PR_TRUE;
+ PRUint32 closeFlags = (aIntent == eShutdownIntent_Error)
+ ? (PRUint32) imgIDecoder::CLOSE_FLAG_DONTNOTIFY
+ : 0;
+ rv = mDecoder->Close(closeFlags);
+ mInDecoder = PR_FALSE;
+
+ // null out the decoder, _then_ check for errors on the close (otherwise the
+ // error routine might re-invoke ShutdownDecoder)
+ mDecoder = nsnull;
+ if (NS_FAILED(rv)) {
+ DoError();
+ return rv;
+ }
+
+ // Get rid of the stream
+ mDecoderInput = nsnull;
+
+ // Kill off the worker
+ mWorker = nsnull;
+
+ // We just shut down the decoder. If we didn't get what we want, but expected
+ // to, flag an error
+ PRBool failed = PR_FALSE;
+ if ((mDecoderFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && !mHasSize)
+ failed = PR_TRUE;
+ if (!(mDecoderFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && !mDecoded)
+ failed = PR_TRUE;
+ if ((aIntent == eShutdownIntent_Done) && failed) {
+ DoError();
+ return NS_ERROR_FAILURE;
+ }
+
+ // Clear the flags
+ mDecoderFlags = imgIDecoder::DECODER_FLAG_NONE;
+
+ // Reset number of decoded bytes
+ mBytesDecoded = 0;
+
+ return NS_OK;
+}
+
+// Wraps a shared stream around the data and passes the stream to the decoder,
+// updating the total number of bytes written.
+nsresult
+imgContainer::WriteToDecoder(const char *aBuffer, PRUint32 aCount)
+{
+ // We should have a decoder
+ NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
+
+ // Wrap a shared stream around the data
+ nsresult rv = mDecoderInput->ShareData(aBuffer, aCount);
+ CONTAINER_ENSURE_SUCCESS(rv);
+
+ // Write
+ mInDecoder = PR_TRUE;
+ rv = mDecoder->WriteFrom(mDecoderInput, aCount);
+ mInDecoder = PR_FALSE;
+ CONTAINER_ENSURE_SUCCESS(rv);
+
+ // Keep track of the total number of bytes written over the lifetime of the
+ // decoder
+ mBytesDecoded += aCount;
+
+ return NS_OK;
+}
+
+// This function is called in situations where it's clear that we want the
+// frames in decoded form (Draw, GetFrame, CopyFrame, ExtractFrame, etc).
+// If we're completely decoded, this method resets the discard timer (if
+// we're discardable), since wanting the frames now is a good indicator of
+// wanting them again soon. If we're not decoded, this method kicks off
+// asynchronous decoding to generate the frames.
+nsresult
+imgContainer::WantDecodedFrames()
+{
+ nsresult rv;
+
+ // If we can discard, we should have a timer already. reset it.
+ if (CanDiscard()) {
+ NS_ABORT_IF_FALSE(mDiscardTimer, "Decoded and discardable but timer not set!");
+ rv = ResetDiscardTimer();
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // Request a decode (no-op if we're decoded)
+ return RequestDecode();
+}
+
+//******************************************************************************
+/* void requestDecode() */
+NS_IMETHODIMP
+imgContainer::RequestDecode()
+{
+ nsresult rv;
+
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // If we're not storing source data, we have nothing to do
+ if (!StoringSourceData())
return NS_OK;
- int num_expected_frames = mNumFrames;
-
- // To prevent that ReloadImages is called multiple times, reset the flag before reloading
- mDiscarded = PR_FALSE;
-
- rv = ReloadImages();
- NS_ENSURE_SUCCESS(rv, rv);
-
- NS_ASSERTION (mNumFrames == PRInt32(mFrames.Length()),
- "number of restored image frames doesn't match");
- NS_ASSERTION (num_expected_frames == mNumFrames,
- "number of restored image frames doesn't match the original number of frames!");
-
- PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
- ("CompressedImageAccounting: imgContainer::RestoreDiscardedData() restored discarded data "
- "for imgContainer %p (%s) - %d image frames. "
- "Compressed containers: %d, Compressed data bytes: %lld",
- this,
- mDiscardableMimeType.get(),
- mNumFrames,
- num_containers_with_discardable_data,
- num_compressed_image_bytes));
-
+ // If we're fully decoded, we have nothing to do
+ if (mDecoded)
+ return NS_OK;
+
+ // If we're within the decoder, request asynchronously
+ if (mInDecoder) {
+ nsRefPtr<imgDecodeRequestor> requestor = new imgDecodeRequestor(this);
+ if (!requestor)
+ return NS_ERROR_OUT_OF_MEMORY;
+ return NS_DispatchToCurrentThread(requestor);
+ }
+
+
+ // If we have a header-only decoder open, interrupt it and shut it down
+ if (mDecoder && (mDecoderFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)) {
+ rv = ShutdownDecoder(eShutdownIntent_Interrupted);
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // If we don't have a decoder, create one
+ if (!mDecoder) {
+ NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
+ rv = InitDecoder(imgIDecoder::DECODER_FLAG_NONE);
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // If we already have a pending worker, we're done
+ if (mWorkerPending)
+ return NS_OK;
+
+ // If we've read all the data we have, we're done
+ if (mBytesDecoded == mSourceData.Length())
+ return NS_OK;
+
+ // If we get this far, dispatch the worker. We do this instead of starting
+ // any immediate decoding so that actions like tabbing-over to a tab with
+ // large undecoded images don't incur an annoying lag.
+ return mWorker->Dispatch();
+}
+
+// Synchronously decodes as much data as possible
+nsresult
+imgContainer::SyncDecode()
+{
+ nsresult rv;
+
+ // If we're decoded already, no worries
+ if (mDecoded)
+ return NS_OK;
+
+ // If we're not storing source data, there isn't much to do here
+ if (!StoringSourceData())
+ return NS_OK;
+
+ // We really have no good way of forcing a synchronous decode if we're being
+ // called in a re-entrant manner (ie, from an event listener fired by a
+ // decoder), because the decoding machinery is already tied up. The best we
+ // can do is assert that it doesn't happen for debug builds (if it does, we
+ // need to rethink the layout code that does it), and fail for release builds.
+ NS_ABORT_IF_FALSE(!mInDecoder, "Yikes, forcing sync in reentrant call!");
+ if (mInDecoder)
+ return NS_ERROR_FAILURE;
+
+ // If we have a header-only decoder open, shut it down
+ if (mDecoder && (mDecoderFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)) {
+ rv = ShutdownDecoder(eShutdownIntent_Interrupted);
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // If we don't have a decoder, create one
+ if (!mDecoder) {
+ NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
+ rv = InitDecoder(imgIDecoder::DECODER_FLAG_NONE);
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // Write everything we have
+ rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
+ mSourceData.Length() - mBytesDecoded);
+ CONTAINER_ENSURE_SUCCESS(rv);
+
+ // If we finished the decode, shutdown the decoder
+ if (IsDecodeFinished()) {
+ rv = ShutdownDecoder(eShutdownIntent_Done);
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
+ // All good!
return NS_OK;
}
//******************************************************************************
-/* [noscript] void draw(in gfxContext aContext, in gfxGraphicsFilter aFilter, in gfxMatrix aUserSpaceToImageSpace, in gfxRect aFill, in nsIntRect aSubimage); */
+/* [noscript] void draw(in gfxContext aContext, in gfxGraphicsFilter aFilter,
+ * in gfxMatrix aUserSpaceToImageSpace, in gfxRect aFill, in nsIntRect aSubimage,
+ * in PRUint32 aFlags); */
NS_IMETHODIMP imgContainer::Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter,
gfxMatrix &aUserSpaceToImageSpace, gfxRect &aFill,
- nsIntRect &aSubimage)
+ nsIntRect &aSubimage, PRUint32 aFlags)
{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
NS_ENSURE_ARG_POINTER(aContext);
+ // If a synchronous draw is requested, flush anything that might be sitting around
+ if (aFlags & FLAG_SYNC_DECODE) {
+ nsresult rv = SyncDecode();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
imgFrame *frame = GetCurrentImgFrame();
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
+ if (!frame) {
+ NS_ABORT_IF_FALSE(!mDecoded, "Decoded but frame not available?");
+ return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
+ }
nsIntRect framerect = frame->GetRect();
nsIntMargin padding(framerect.x, framerect.y,
mSize.width - framerect.XMost(),
mSize.height - framerect.YMost());
frame->Draw(aContext, aFilter, aUserSpaceToImageSpace, aFill, padding, aSubimage);
return NS_OK;
}
-class ContainerLoader : public imgILoad,
- public imgIDecoderObserver,
- public nsSupportsWeakReference
-{
-public:
-
- NS_DECL_ISUPPORTS
- NS_DECL_IMGILOAD
- NS_DECL_IMGIDECODEROBSERVER
- NS_DECL_IMGICONTAINEROBSERVER
-
- ContainerLoader(void);
-
-private:
-
- nsCOMPtr<imgIContainer> mContainer;
-};
-
-NS_IMPL_ISUPPORTS4 (ContainerLoader, imgILoad, imgIDecoderObserver, imgIContainerObserver, nsISupportsWeakReference)
-
-ContainerLoader::ContainerLoader (void)
+//******************************************************************************
+/* void lockImage() */
+NS_IMETHODIMP
+imgContainer::LockImage()
{
-}
-
-/* Implement imgILoad::image getter */
-NS_IMETHODIMP
-ContainerLoader::GetImage(imgIContainer **aImage)
-{
- *aImage = mContainer;
- NS_IF_ADDREF (*aImage);
- return NS_OK;
-}
-
-/* Implement imgILoad::image setter */
-NS_IMETHODIMP
-ContainerLoader::SetImage(imgIContainer *aImage)
-{
- mContainer = aImage;
- return NS_OK;
-}
-
-/* Implement imgILoad::isMultiPartChannel getter */
-NS_IMETHODIMP
-ContainerLoader::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel)
-{
- *aIsMultiPartChannel = PR_FALSE; /* FIXME: is this always right? */
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // Cancel the discard timer if it's there
+ if (mDiscardTimer) {
+ mDiscardTimer->Cancel();
+ mDiscardTimer = nsnull; // It's wasteful to null out the discard timers each
+ // time, but we'll wait to fix that until bug 502694.
+ }
+
+ // Increment the lock count
+ mLockCount++;
+
return NS_OK;
}
-/* Implement imgIDecoderObserver::onStartRequest() */
-NS_IMETHODIMP
-ContainerLoader::OnStartRequest(imgIRequest *aRequest)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStartDecode() */
-NS_IMETHODIMP
-ContainerLoader::OnStartDecode(imgIRequest *aRequest)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStartContainer() */
-NS_IMETHODIMP
-ContainerLoader::OnStartContainer(imgIRequest *aRequest, imgIContainer *aContainer)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStartFrame() */
+//******************************************************************************
+/* void unlockImage() */
NS_IMETHODIMP
-ContainerLoader::OnStartFrame(imgIRequest *aRequest, PRUint32 aFrame)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onDataAvailable() */
-NS_IMETHODIMP
-ContainerLoader::OnDataAvailable(imgIRequest *aRequest, PRBool aCurrentFrame, const nsIntRect * aRect)
+imgContainer::UnlockImage()
{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStopFrame() */
-NS_IMETHODIMP
-ContainerLoader::OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStopContainer() */
-NS_IMETHODIMP
-ContainerLoader::OnStopContainer(imgIRequest *aRequest, imgIContainer *aContainer)
-{
+ if (mError)
+ return NS_ERROR_FAILURE;
+
+ // It's an error to call this function if the lock count is 0
+ NS_ABORT_IF_FALSE(mLockCount > 0,
+ "Calling UnlockImage with mLockCount == 0!");
+ if (mLockCount == 0)
+ return NS_ERROR_ABORT;
+
+ // We're locked, so we shouldn't have a discard timer set
+ NS_ABORT_IF_FALSE(!mDiscardTimer, "Locked, but discard timer set!");
+
+ // Decrement our lock count
+ mLockCount--;
+
+ // We now _might_ have one of the qualifications for discarding. Re-evaluate.
+ if (CanDiscard()) {
+ nsresult rv = ResetDiscardTimer();
+ CONTAINER_ENSURE_SUCCESS(rv);
+ }
+
return NS_OK;
}
-/* Implement imgIDecoderObserver::onStopDecode() */
-NS_IMETHODIMP
-ContainerLoader::OnStopDecode(imgIRequest *aRequest, nsresult status, const PRUnichar *statusArg)
+// Flushes up to aMaxBytes to the decoder.
+nsresult
+imgContainer::DecodeSomeData (PRUint32 aMaxBytes)
+{
+ // We should have a decoder if we get here
+ NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
+
+ // If we have nothing to decode, return
+ if (mBytesDecoded == mSourceData.Length())
+ return NS_OK;
+
+
+ // write the proper amount of data
+ PRUint32 bytesToDecode = PR_MIN(aMaxBytes,
+ mSourceData.Length() - mBytesDecoded);
+ nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
+ bytesToDecode);
+
+ return rv;
+}
+
+// There are various indicators that tell us we're finished with the decode
+// task at hand and can shut down the decoder.
+PRBool imgContainer::IsDecodeFinished()
{
- return NS_OK;
+ // Assume it's not finished
+ PRBool decodeFinished = PR_FALSE;
+
+ // There shouldn't be any reason to call this if we're not storing
+ // source data
+ NS_ABORT_IF_FALSE(StoringSourceData(),
+ "just shut down on SourceDataComplete!");
+
+ // The decode is complete if we got what we wanted...
+ if (mDecoderFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) {
+ if (mHasSize)
+ decodeFinished = PR_TRUE;
+ }
+ else {
+ if (mDecoded)
+ decodeFinished = PR_TRUE;
+ }
+
+ // ...or if we have all the source data and wrote all the source data.
+ //
+ // (NB - This can be distinct from the above case even for non-erroneous
+ // images because the decoder might not call DecodingComplete() until we
+ // call Close() in ShutdownDecoder())
+ if (mHasSourceData && (mBytesDecoded == mSourceData.Length()))
+ decodeFinished = PR_TRUE;
+
+ return decodeFinished;
+}
+
+// Indempotent error flagging routine. If a decoder is open,
+// sends OnStopContainer and OnStopDecode and shuts down the decoder
+void imgContainer::DoError()
+{
+ // If we've flagged an error before, we have nothing to do
+ if (mError)
+ return;
+
+ // If we're mid-decode
+ if (mDecoder) {
+
+ // grab the observer and give an OnStopContainer and an OnStopDecode
+ nsCOMPtr<imgIDecoderObserver> observer = do_QueryReferent(mObserver);
+ if (observer) {
+ observer->OnStopContainer(nsnull, this);
+ observer->OnStopDecode(nsnull, NS_ERROR_FAILURE, nsnull);
+ }
+
+ // Shutdown the decoder in error mode. We don't care if this flags other
+ // errors.
+ (void) ShutdownDecoder(eShutdownIntent_Error);
+ }
+
+ // Put the container in an error state
+ mError = PR_TRUE;
+
+ // Log our error
+ LOG_CONTAINER_ERROR;
}
-/* Implement imgIDecoderObserver::onStopRequest() */
-NS_IMETHODIMP
-ContainerLoader::OnStopRequest(imgIRequest *aRequest, PRBool aIsLastPart)
+// Tweakable progressive decoding parameters
+#define DECODE_BYTES_AT_A_TIME 4096
+#define MAX_USEC_BEFORE_YIELD (1000 * 5)
+
+// Decodes some data, then re-posts itself to the end of the event queue if
+// there's more processing to be done
+NS_IMETHODIMP imgDecodeWorker::Run()
{
- return NS_OK;
-}
-
-/* implement imgIContainerObserver::frameChanged() */
-NS_IMETHODIMP
-ContainerLoader::FrameChanged(imgIContainer *aContainer, nsIntRect * aDirtyRect)
-{
+ nsresult rv;
+
+ // If we shutdown the decoder in this function, we could lose ourselves
+ nsCOMPtr<nsIRunnable> kungFuDeathGrip(this);
+
+ // The container holds a strong reference to us. Cycles are bad.
+ nsCOMPtr<imgIContainer> iContainer(do_QueryReferent(mContainer));
+ if (!iContainer)
+ return NS_OK;
+ imgContainer* container = static_cast<imgContainer*>(iContainer.get());
+
+ NS_ABORT_IF_FALSE(container->mInitialized,
+ "Worker active for uninitialized container!");
+
+ // If we were pending, we're not anymore
+ container->mWorkerPending = PR_FALSE;
+
+ // If an error is flagged, it probably happened while we were waiting
+ // in the event queue. Bail early, but no need to bother the run queue
+ // by returning an error.
+ if (container->mError)
+ return NS_OK;
+
+ // If we don't have a decoder, we must have finished already (for example,
+ // a synchronous decode request came while the worker was pending).
+ if (!container->mDecoder)
+ return NS_OK;
+
+ // Header-only decodes are cheap and we more or less want them to be
+ // synchronous. Write all the data in that case, otherwise write a
+ // chunk
+ PRUint32 maxBytes =
+ (container->mDecoderFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
+ ? container->mSourceData.Length() : DECODE_BYTES_AT_A_TIME;
+
+ // Loop control
+ PRBool haveMoreData = PR_TRUE;
+ nsTime deadline(PR_Now() + MAX_USEC_BEFORE_YIELD);
+
+ // We keep decoding chunks until one of three possible events occur:
+ // 1) We don't have any data left to decode
+ // 2) The decode completes
+ // 3) We hit the deadline and need to yield to keep the UI snappy
+ while (haveMoreData && !container->IsDecodeFinished() &&
+ (nsTime(PR_Now()) < deadline)) {
+
+ // Decode a chunk of data
+ rv = container->DecodeSomeData(maxBytes);
+ if (NS_FAILED(rv)) {
+ container->DoError();
+ return rv;
+ }
+
+ // Figure out if we still have more data
+ haveMoreData =
+ container->mSourceData.Length() > container->mBytesDecoded;
+ }
+
+ // If the decode finished, shutdown the decoder
+ if (container->IsDecodeFinished()) {
+ rv = container->ShutdownDecoder(imgContainer::eShutdownIntent_Done);
+ if (NS_FAILED(rv)) {
+ container->DoError();
+ return rv;
+ }
+ }
+
+ // If Conditions 1 & 2 are still true, then the only reason we bailed was
+ // because we hit the deadline. Repost ourselves to the end of the event
+ // queue.
+ if (!container->IsDecodeFinished() && haveMoreData)
+ return this->Dispatch();
+
+ // Otherwise, return success
return NS_OK;
}
-nsresult
-imgContainer::ReloadImages(void)
+// Queues the worker up at the end of the event queue
+NS_METHOD imgDecodeWorker::Dispatch()
{
- NS_ASSERTION(!mRestoreData.IsEmpty(),
- "imgContainer::ReloadImages(): mRestoreData should not be empty");
- NS_ASSERTION(mRestoreDataDone,
- "imgContainer::ReloadImages(): mRestoreDataDone shoudl be true!");
-
- mNumFrames = 0;
- NS_ASSERTION(mFrames.Length() == 0,
- "imgContainer::ReloadImages(): mFrames should be empty");
-
- nsCAutoString decoderCID(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mDiscardableMimeType);
-
- nsCOMPtr<imgIDecoder> decoder = do_CreateInstance(decoderCID.get());
- if (!decoder) {
- PR_LOG(gCompressedImageAccountingLog, PR_LOG_WARNING,
- ("CompressedImageAccounting: imgContainer::ReloadImages() could not create decoder for %s",
- mDiscardableMimeType.get()));
- return NS_IMAGELIB_ERROR_NO_DECODER;
- }
-
- nsCOMPtr<imgILoad> loader = new ContainerLoader();
- if (!loader) {
- PR_LOG(gCompressedImageAccountingLog, PR_LOG_WARNING,
- ("CompressedImageAccounting: imgContainer::ReloadImages() could not allocate ContainerLoader "
- "when reloading the images for container %p",
- this));
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- loader->SetImage(this);
-
- nsresult result = decoder->Init(loader);
- if (NS_FAILED(result)) {
- PR_LOG(gCompressedImageAccountingLog, PR_LOG_WARNING,
- ("CompressedImageAccounting: imgContainer::ReloadImages() image container %p "
- "failed to initialize the decoder (%s)",
- this,
- mDiscardableMimeType.get()));
- return result;
- }
-
- nsCOMPtr<nsIInputStream> stream;
- result = NS_NewByteInputStream(getter_AddRefs(stream), mRestoreData.Elements(), mRestoreData.Length(), NS_ASSIGNMENT_DEPEND);
- NS_ENSURE_SUCCESS(result, result);
-
- if (PR_LOG_TEST(gCompressedImageAccountingLog, PR_LOG_DEBUG)) {
- char buf[9];
- get_header_str(buf, mRestoreData.Elements(), mRestoreData.Length());
- PR_LOG(gCompressedImageAccountingLog, PR_LOG_WARNING,
- ("CompressedImageAccounting: imgContainer::ReloadImages() starting to restore images for container %p (%s) - "
- "header %p is 0x%s (length %d)",
- this,
- mDiscardableMimeType.get(),
- mRestoreData.Elements(),
- buf,
- mRestoreData.Length()));
- }
-
- // |WriteFrom()| may fail if the original data is broken.
- PRUint32 written;
- (void)decoder->WriteFrom(stream, mRestoreData.Length(), &written);
-
- result = decoder->Flush();
- NS_ENSURE_SUCCESS(result, result);
-
- result = decoder->Close();
- NS_ENSURE_SUCCESS(result, result);
-
- NS_ASSERTION(PRInt32(mFrames.Length()) == mNumFrames,
- "imgContainer::ReloadImages(): the restored mFrames.Length() doesn't match mNumFrames!");
-
- return result;
+ // The container holds a strong reference to us. Cycles are bad.
+ nsCOMPtr<imgIContainer> iContainer(do_QueryReferent(mContainer));
+ if (!iContainer)
+ return NS_OK;
+ imgContainer* container = static_cast<imgContainer*>(iContainer.get());
+
+ // We should not be called if there's already a pending worker
+ NS_ABORT_IF_FALSE(!container->mWorkerPending,
+ "Trying to queue up worker with one already pending!");
+
+ // Flag that we're pending
+ container->mWorkerPending = PR_TRUE;
+
+ // Dispatch
+ return NS_DispatchToCurrentThread(this);
}
+
+// nsIInputStream callback to copy the incoming image data directly to the
+// container without processing. The imgContainer is passed as the closure.
+// Always reads everything it gets, even if the data is erroneous.
+NS_METHOD
+imgContainer::WriteToContainer(nsIInputStream* in, void* closure,
+ const char* fromRawSegment, PRUint32 toOffset,
+ PRUint32 count, PRUint32 *writeCount)
+{
+ // Retrieve the imgContainer
+ imgIContainer *container = static_cast<imgIContainer*>(closure);
+
+ // Copy the source data. We squelch the return value here, because returning
+ // an error means that ReadSegments stops reading data, violating our
+ // invariant that we read everything we get.
+ (void) container->AddSourceData(fromRawSegment, count);
+
+ // We wrote everything we got
+ *writeCount = count;
+
+ return NS_OK;
+}
--- a/modules/libpr0n/src/imgContainer.h
+++ b/modules/libpr0n/src/imgContainer.h
@@ -19,16 +19,17 @@
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
* Chris Saari <saari@netscape.com>
* Federico Mena-Quintero <federico@novell.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -50,21 +51,24 @@
*/
#ifndef __imgContainer_h__
#define __imgContainer_h__
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "imgIContainer.h"
+#include "imgIDecoder.h"
#include "nsIProperties.h"
#include "nsITimer.h"
#include "nsWeakReference.h"
#include "nsTArray.h"
+#include "nsIStringStream.h"
#include "imgFrame.h"
+#include "nsThreadUtils.h"
#define NS_IMGCONTAINER_CID \
{ /* c76ff2c1-9bf6-418a-b143-3340c00112f7 */ \
0x376ff2c1, \
0x9bf6, \
0x418a, \
{0xb1, 0x43, 0x33, 0x40, 0xc0, 0x01, 0x12, 0xf7} \
}
@@ -126,37 +130,44 @@
* The mAnim structure has members only needed for animated images, so
* it's not allocated until the second frame is added.
*
* @note
* mAnimationMode, mLoopCount and mObserver are not in the mAnim structure
* because the first two have public setters and the observer we only get
* in Init().
*/
+class imgDecodeWorker;
class imgContainer : public imgIContainer,
- public nsITimerCallback,
- public nsIProperties
+ public nsITimerCallback,
+ public nsIProperties,
+ public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IMGICONTAINER
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSIPROPERTIES
imgContainer();
virtual ~imgContainer();
+ static NS_METHOD WriteToContainer(nsIInputStream* in, void* closure,
+ const char* fromRawSegment,
+ PRUint32 toOffset, PRUint32 count,
+ PRUint32 *writeCount);
+
private:
struct Anim
{
//! Area of the first frame that needs to be redrawn on subsequent loops.
nsIntRect firstFrameRefreshArea;
// Note this doesn't hold a proper value until frame 2 finished decoding.
- PRInt32 currentDecodingFrameIndex; // 0 to numFrames-1
- PRInt32 currentAnimationFrameIndex; // 0 to numFrames-1
+ PRUint32 currentDecodingFrameIndex; // 0 to numFrames-1
+ PRUint32 currentAnimationFrameIndex; // 0 to numFrames-1
//! Track the last composited frame for Optimizations (See DoComposite code)
PRInt32 lastCompositedFrameIndex;
//! Whether we can assume there will be no more frames
//! (and thus loop the animation)
PRBool doneDecoding;
//! Are we currently animating the image?
PRBool animating;
/** For managing blending of frames
@@ -192,21 +203,36 @@ private:
{
if (timer)
timer->Cancel();
}
};
imgFrame* GetImgFrame(PRUint32 framenum);
imgFrame* GetCurrentImgFrame();
- PRInt32 GetCurrentImgFrameIndex() const;
+ PRUint32 GetCurrentImgFrameIndex() const;
- inline Anim* ensureAnimExists() {
- if (!mAnim)
+ inline Anim* ensureAnimExists()
+ {
+ if (!mAnim) {
+
+ // Create the animation context
mAnim = new Anim();
+
+ // We don't support discarding animated images (See bug 414259)
+ // Flag that we are no longer discardable (if we were before)
+ // and cancel any discard timer.
+ mDiscardable = PR_FALSE;
+ if (mDiscardTimer) {
+ nsresult rv = mDiscardTimer->Cancel();
+ if (!NS_SUCCEEDED(rv))
+ NS_WARNING("Discard Timer failed to cancel!");
+ mDiscardTimer = nsnull;
+ }
+ }
return mAnim;
}
/** Function for doing the frame compositing of animations
*
* @param aFrameToUse Set by DoComposite
* (aNextFrame, compositingFrame, or compositingPrevFrame)
* @param aDirtyRect Area that the display will need to update
@@ -251,45 +277,143 @@ private:
nsresult InternalAddFrame(PRUint32 framenum, PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight,
gfxASurface::gfxImageFormat aFormat, PRUint8 aPaletteDepth,
PRUint8 **imageData, PRUint32 *imageLength,
PRUint32 **paletteData, PRUint32 *paletteLength);
private: // data
nsIntSize mSize;
+ PRBool mHasSize;
//! All the frames of the image
- // *** IMPORTANT: if you use mFrames in a method, call RestoreDiscardedData() first to ensure
- // that the frames actually exist (they may have been discarded to save memory).
+ // IMPORTANT: if you use mFrames in a method, call EnsureImageIsDecoded() first
+ // to ensure that the frames actually exist (they may have been discarded to save
+ // memory, or we may be decoding on draw).
nsTArray<imgFrame *> mFrames;
- int mNumFrames; /* stored separately from mFrames.Count() to support discarded images */
nsCOMPtr<nsIProperties> mProperties;
- // *** IMPORTANT: if you use mAnim in a method, call RestoreDiscardedData() first to ensure
- // that the frames actually exist (they may have been discarded to save memory).
+ // IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
+ // that the frames actually exist (they may have been discarded to save memory, or
+ // we maybe decoding on draw).
imgContainer::Anim* mAnim;
//! See imgIContainer for mode constants
PRUint16 mAnimationMode;
//! # loops remaining before animation stops (-1 no stop)
PRInt32 mLoopCount;
- //! imgIContainerObserver
+ //! imgIDecoderObserver
nsWeakPtr mObserver;
+ // Decoding on draw?
+ PRBool mDecodeOnDraw;
+
+ // Multipart?
+ PRBool mMultipart;
+
+ // Have we been initalized?
+ PRBool mInitialized;
+
+ // Discard members
PRBool mDiscardable;
- PRBool mDiscarded;
- nsCString mDiscardableMimeType;
-
- nsTArray<char> mRestoreData;
- PRBool mRestoreDataDone;
+ PRUint32 mLockCount;
nsCOMPtr<nsITimer> mDiscardTimer;
- nsresult ResetDiscardTimer (void);
- nsresult RestoreDiscardedData (void);
- nsresult ReloadImages (void);
- static void sDiscardTimerCallback (nsITimer *aTimer, void *aClosure);
+ // Source data members
+ nsTArray<char> mSourceData;
+ PRBool mHasSourceData;
+ nsCString mSourceDataMimeType;
+
+ // Do we have the frames in decoded form?
+ PRBool mDecoded;
+
+ friend class imgDecodeWorker;
+
+ // Decoder and friends
+ nsCOMPtr<imgIDecoder> mDecoder;
+ nsRefPtr<imgDecodeWorker> mWorker;
+ PRUint32 mBytesDecoded;
+ nsCOMPtr<nsIStringInputStream> mDecoderInput;
+ PRUint32 mDecoderFlags;
+ PRBool mWorkerPending;
+ PRBool mInDecoder;
+
+ // Error handling
+ PRBool mError;
+
+ // Discard code
+ nsresult ResetDiscardTimer();
+ static void sDiscardTimerCallback(nsITimer *aTimer, void *aClosure);
+
+ // Decoding
+ nsresult WantDecodedFrames();
+ nsresult SyncDecode();
+ nsresult InitDecoder(PRUint32 dFlags);
+ nsresult WriteToDecoder(const char *aBuffer, PRUint32 aCount);
+ nsresult DecodeSomeData(PRUint32 aMaxBytes);
+ PRBool IsDecodeFinished();
+ nsresult EnableDiscarding();
+
+ // Decoder shutdown
+ enum eShutdownIntent {
+ eShutdownIntent_Done = 0,
+ eShutdownIntent_Interrupted = 1,
+ eShutdownIntent_Error = 2,
+ eShutdownIntent_AllCount = 3
+ };
+ nsresult ShutdownDecoder(eShutdownIntent aIntent);
+
+ // Helpers
+ void DoError();
+ PRBool CanDiscard();
+ PRBool StoringSourceData();
+
};
+// Decoding Helper Class
+//
+// We use this class to mimic the interactivity benefits of threading
+// in a single-threaded event loop. We want to progressively decode
+// and keep a responsive UI while we're at it, so we have a runnable
+// class that does a bit of decoding, and then "yields" by dispatching
+// itself to the end of the event queue.
+class imgDecodeWorker : public nsRunnable
+{
+ public:
+ imgDecodeWorker(imgIContainer* aContainer) {
+ mContainer = do_GetWeakReference(aContainer);
+ }
+ NS_IMETHOD Run();
+ NS_METHOD Dispatch();
+
+ private:
+ nsWeakPtr mContainer;
+};
+
+// Asynchronous Decode Requestor
+//
+// We use this class when someone calls requestDecode() from within a decode
+// notification. Since requestDecode() involves modifying the decoder's state
+// (for example, possibly shutting down a header-only decode and starting a
+// full decode), we don't want to do this from inside a decoder.
+class imgDecodeRequestor : public nsRunnable
+{
+ public:
+ imgDecodeRequestor(imgIContainer *aContainer) {
+ mContainer = do_GetWeakReference(aContainer);
+ }
+ NS_IMETHOD Run() {
+ nsCOMPtr<imgIContainer> con = do_QueryReferent(mContainer);
+ if (con)
+ con->RequestDecode();
+ return NS_OK;
+ }
+
+ private:
+ nsWeakPtr mContainer;
+};
+
+
+
#endif /* __imgContainer_h__ */
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -97,17 +97,17 @@ static void PrintImageDecoders()
while (NS_SUCCEEDED(enumer->HasMoreElements(&more)) && more) {
enumer->GetNext(getter_AddRefs(s));
if (s) {
nsCOMPtr<nsISupportsCString> ss(do_QueryInterface(s));
nsCAutoString xcs;
ss->GetData(xcs);
- NS_NAMED_LITERAL_CSTRING(decoderContract, "@mozilla.org/image/decoder;2?type=");
+ NS_NAMED_LITERAL_CSTRING(decoderContract, "@mozilla.org/image/decoder;3?type=");
if (StringBeginsWith(xcs, decoderContract)) {
printf("Have decoder for mime type: %s\n", xcs.get()+decoderContract.Length());
}
}
}
}
#endif
@@ -367,44 +367,34 @@ imgCacheEntry::imgCacheEntry(imgRequest
mHasNoProxies(PR_TRUE)
{}
imgCacheEntry::~imgCacheEntry()
{
LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()");
}
-void imgCacheEntry::TouchWithSize(PRInt32 diff)
-{
- LOG_SCOPE(gImgLog, "imgCacheEntry::TouchWithSize");
-
- mTouchedTime = SecondsFromPRTime(PR_Now());
-
- // Don't update the cache if we've been removed from it or it doesn't care
- // about our size or usage.
- if (!Evicted() && HasNoProxies()) {
- nsCOMPtr<nsIURI> uri;
- mRequest->GetKeyURI(getter_AddRefs(uri));
- imgLoader::CacheEntriesChanged(uri, diff);
- }
-}
-
void imgCacheEntry::Touch(PRBool updateTime /* = PR_TRUE */)
{
LOG_SCOPE(gImgLog, "imgCacheEntry::Touch");
if (updateTime)
mTouchedTime = SecondsFromPRTime(PR_Now());
+ UpdateCache();
+}
+
+void imgCacheEntry::UpdateCache(PRInt32 diff /* = 0 */)
+{
// Don't update the cache if we've been removed from it or it doesn't care
// about our size or usage.
if (!Evicted() && HasNoProxies()) {
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
- imgLoader::CacheEntriesChanged(uri);
+ imgLoader::CacheEntriesChanged(uri, diff);
}
}
void imgCacheEntry::SetHasNoProxies(PRBool hasNoProxies)
{
#if defined(PR_LOGGING)
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
@@ -1636,17 +1626,17 @@ NS_IMETHODIMP imgLoader::SupportImageWit
{
*_retval = PR_FALSE;
nsCOMPtr<nsIComponentRegistrar> reg;
nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(reg));
if (NS_FAILED(rv))
return rv;
nsCAutoString mimeType(aMimeType);
ToLowerCase(mimeType);
- nsCAutoString decoderId(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mimeType);
+ nsCAutoString decoderId(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;3?type=") + mimeType);
return reg->IsContractIDRegistered(decoderId.get(), _retval);
}
NS_IMETHODIMP imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest,
const PRUint8* aContents,
PRUint32 aLength,
nsACString& aContentType)
{
--- a/modules/libpr0n/src/imgLoader.h
+++ b/modules/libpr0n/src/imgLoader.h
@@ -92,17 +92,17 @@ public:
PRUint32 GetDataSize() const
{
return mDataSize;
}
void SetDataSize(PRUint32 aDataSize)
{
PRInt32 oldsize = mDataSize;
mDataSize = aDataSize;
- TouchWithSize(mDataSize - oldsize);
+ UpdateCache(mDataSize - oldsize);
}
PRInt32 GetTouchedTime() const
{
return mTouchedTime;
}
void SetTouchedTime(PRInt32 time)
{
@@ -151,17 +151,17 @@ public:
{
return mHasNoProxies;
}
private: // methods
friend class imgLoader;
friend class imgCacheQueue;
void Touch(PRBool updateTime = PR_TRUE);
- void TouchWithSize(PRInt32 diff);
+ void UpdateCache(PRInt32 diff = 0);
void SetEvicted(PRBool evict)
{
mEvicted = evict;
}
void SetHasNoProxies(PRBool hasNoProxies);
// Private, unimplemented copy constructor.
imgCacheEntry(const imgCacheEntry &);
--- a/modules/libpr0n/src/imgRequest.cpp
+++ b/modules/libpr0n/src/imgRequest.cpp
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -36,16 +37,17 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "imgRequest.h"
#include "imgLoader.h"
#include "imgRequestProxy.h"
+#include "imgContainer.h"
#include "imgILoader.h"
#include "ImageErrors.h"
#include "ImageLogging.h"
#include "netCore.h"
#include "nsIChannel.h"
@@ -63,31 +65,34 @@
#include "nsIScriptSecurityManager.h"
#include "nsICacheVisitor.h"
#include "nsString.h"
#include "nsXPIDLString.h"
#include "plstr.h" // PL_strcasestr(...)
+static PRBool gDecodeOnDraw = PR_TRUE;
+static PRBool gDiscardable = PR_TRUE;
+
#if defined(PR_LOGGING)
PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest");
#endif
-NS_IMPL_ISUPPORTS8(imgRequest, imgILoad,
+NS_IMPL_ISUPPORTS7(imgRequest,
imgIDecoderObserver, imgIContainerObserver,
nsIStreamListener, nsIRequestObserver,
nsISupportsWeakReference,
nsIChannelEventSink,
nsIInterfaceRequestor)
imgRequest::imgRequest() :
mImageStatus(imgIRequest::STATUS_NONE), mState(0), mCacheId(0),
mValidator(nsnull), mImageSniffers("image-sniffing-services"),
- mIsMultiPartChannel(PR_FALSE), mLoading(PR_FALSE), mProcessing(PR_FALSE),
+ mIsMultiPartChannel(PR_FALSE), mLoading(PR_FALSE),
mHadLastPart(PR_FALSE), mGotData(PR_FALSE), mIsInCache(PR_FALSE)
{
/* member initializers and constructor code */
}
imgRequest::~imgRequest()
{
if (mKeyURI) {
@@ -181,24 +186,24 @@ nsresult imgRequest::RemoveProxy(imgRequ
/* Check mState below before we potentially call Cancel() below. Since
Cancel() may result in OnStopRequest being called back before Cancel()
returns, leaving mState in a different state then the one it was in at
this point.
*/
if (aNotify) {
// make sure that observer gets an OnStopDecode message sent to it
- if (!(mState & onStopDecode)) {
+ if (!(mState & stateRequestStopped)) {
proxy->OnStopDecode(aStatus, nsnull);
}
}
// make sure that observer gets an OnStopRequest message sent to it
- if (!(mState & onStopRequest)) {
+ if (!(mState & stateRequestStopped)) {
proxy->OnStopRequest(nsnull, nsnull, NS_BINDING_ABORTED, PR_TRUE);
}
if (mImage && !HaveProxyWithObserver(nsnull)) {
LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "stopping animation");
mImage->StopAnimation();
}
@@ -245,68 +250,57 @@ nsresult imgRequest::RemoveProxy(imgRequ
return NS_OK;
}
nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy)
{
nsCOMPtr<imgIRequest> kungFuDeathGrip(proxy);
// OnStartRequest
- if (mState & onStartRequest)
+ if (mState & stateRequestStarted)
proxy->OnStartRequest(nsnull, nsnull);
+ // OnStartContainer
+ if (mState & stateHasSize)
+ proxy->OnStartContainer(mImage);
+
// OnStartDecode
- if (mState & onStartDecode)
+ if (mState & stateDecodeStarted)
proxy->OnStartDecode();
- // OnStartContainer
- if (mState & onStartContainer)
- proxy->OnStartContainer(mImage);
-
// Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame)
PRUint32 nframes = 0;
if (mImage)
mImage->GetNumFrames(&nframes);
if (nframes > 0) {
PRUint32 frame;
mImage->GetCurrentFrameIndex(&frame);
proxy->OnStartFrame(frame);
- if (!(mState & onStopContainer)) {
- // OnDataAvailable
- nsIntRect r;
- mImage->GetCurrentFrameRect(r); // XXX we should only send the currently decoded rectangle here.
- proxy->OnDataAvailable(frame, &r);
- } else {
- // OnDataAvailable
- nsIntRect r;
- mImage->GetCurrentFrameRect(r); // We're done loading this image, send the the whole rect
- proxy->OnDataAvailable(frame, &r);
+ // OnDataAvailable
+ // XXX - Should only send partial rects here, but that needs to
+ // wait until we fix up the observer interface
+ nsIntRect r;
+ mImage->GetCurrentFrameRect(r);
+ proxy->OnDataAvailable(frame, &r);
- // OnStopFrame
+ if (mState & stateRequestStopped)
proxy->OnStopFrame(frame);
- }
}
- // OnStopContainer
- if (mState & onStopContainer)
- proxy->OnStopContainer(mImage);
-
- // OnStopDecode
- if (mState & onStopDecode)
- proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
-
if (mImage && !HaveProxyWithObserver(proxy) && proxy->HasObserver()) {
LOG_MSG(gImgLog, "imgRequest::NotifyProxyListener", "resetting animation");
mImage->ResetAnimation();
}
- if (mState & onStopRequest) {
+ if (mState & stateRequestStopped) {
+ proxy->OnStopContainer(mImage);
+ proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
proxy->OnStopRequest(nsnull, nsnull,
GetResultFromImageStatus(mImageStatus),
mHadLastPart);
}
return NS_OK;
}
@@ -332,19 +326,17 @@ void imgRequest::Cancel(nsresult aStatus
LOG_MSG(gImgLog, "imgRequest::Cancel", "stopping animation");
mImage->StopAnimation();
}
if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL))
mImageStatus |= imgIRequest::STATUS_ERROR;
- if (aStatus != NS_IMAGELIB_ERROR_NO_DECODER) {
- RemoveFromCache();
- }
+ RemoveFromCache();
if (mRequest && mLoading)
mRequest->Cancel(aStatus);
}
void imgRequest::CancelAndAbort(nsresult aStatus)
{
LOG_SCOPE(gImgLog, "imgRequest::CancelAndAbort");
@@ -468,45 +460,43 @@ void imgRequest::AdjustPriority(imgReque
}
void imgRequest::SetIsInCache(PRBool incache)
{
LOG_FUNC_WITH_PARAM(gImgLog, "imgRequest::SetIsCacheable", "incache", incache);
mIsInCache = incache;
}
-/** imgILoad methods **/
-
-NS_IMETHODIMP imgRequest::SetImage(imgIContainer *aImage)
+void imgRequest::UpdateCacheEntrySize()
{
- LOG_FUNC(gImgLog, "imgRequest::SetImage");
+ if (mCacheEntry) {
+ PRUint32 imageSize = 0;
+ if (mImage)
+ mImage->GetDataSize(&imageSize);
+ mCacheEntry->SetDataSize(imageSize);
- mImage = aImage;
+#ifdef DEBUG_joe
+ nsCAutoString url;
+ mURI->GetSpec(url);
+ printf("CACHEPUT: %d %s %d\n", time(NULL), url.get(), imageSize);
+#endif
+ }
- return NS_OK;
}
-NS_IMETHODIMP imgRequest::GetImage(imgIContainer **aImage)
+nsresult
+imgRequest::GetImage(imgIContainer **aImage)
{
LOG_FUNC(gImgLog, "imgRequest::GetImage");
*aImage = mImage;
NS_IF_ADDREF(*aImage);
return NS_OK;
}
-NS_IMETHODIMP imgRequest::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel)
-{
- LOG_FUNC(gImgLog, "imgRequest::GetIsMultiPartChannel");
-
- *aIsMultiPartChannel = mIsMultiPartChannel;
-
- return NS_OK;
-}
-
/** imgIContainerObserver methods **/
/* [noscript] void frameChanged (in imgIContainer container, in nsIntRect dirtyRect); */
NS_IMETHODIMP imgRequest::FrameChanged(imgIContainer *container,
nsIntRect * dirtyRect)
{
LOG_SCOPE(gImgLog, "imgRequest::FrameChanged");
@@ -520,17 +510,17 @@ NS_IMETHODIMP imgRequest::FrameChanged(i
/** imgIDecoderObserver methods **/
/* void onStartDecode (in imgIRequest request); */
NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
- mState |= onStartDecode;
+ mState |= stateDecodeStarted;
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStartDecode();
}
/* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
indicates the beginning of a new decode.
@@ -552,23 +542,28 @@ NS_IMETHODIMP imgRequest::OnStartRequest
/* void onStartContainer (in imgIRequest request, in imgIContainer image); */
NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *image)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStartContainer");
NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!");
if (!image) return NS_ERROR_UNEXPECTED;
- mState |= onStartContainer;
+ // We only want to send onStartContainer once
+ PRBool alreadySent = (mState & stateHasSize) != 0;
+
+ mState |= stateHasSize;
mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
- nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
- while (iter.HasMore()) {
- iter.GetNext()->OnStartContainer(image);
+ if (!alreadySent) {
+ nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+ while (iter.HasMore()) {
+ iter.GetNext()->OnStartContainer(image);
+ }
}
return NS_OK;
}
/* void onStartFrame (in imgIRequest request, in unsigned long frame); */
NS_IMETHODIMP imgRequest::OnStartFrame(imgIRequest *request,
PRUint32 frame)
@@ -601,121 +596,144 @@ NS_IMETHODIMP imgRequest::OnDataAvailabl
/* void onStopFrame (in imgIRequest request, in unsigned long frame); */
NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request,
PRUint32 frame)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame");
mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE;
- if (mCacheEntry) {
- PRUint32 cacheSize = mCacheEntry->GetDataSize();
-
- PRUint32 imageSize = 0;
- if (mImage)
- mImage->GetFrameImageDataLength(frame, &imageSize);
-
- mCacheEntry->SetDataSize(cacheSize + imageSize);
-
-#ifdef DEBUG_joe
- nsCAutoString url;
- mURI->GetSpec(url);
-
- printf("CACHEPUT: %d %s %d\n", time(NULL), url.get(), cacheSize + imageSize);
-#endif
- }
-
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStopFrame(frame);
}
return NS_OK;
}
/* void onStopContainer (in imgIRequest request, in imgIContainer image); */
NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request,
imgIContainer *image)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer");
- mState |= onStopContainer;
-
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStopContainer(image);
}
return NS_OK;
}
/* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */
NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
nsresult aStatus,
const PRUnichar *aStatusArg)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStopDecode");
- NS_ASSERTION(!(mState & onStopDecode), "OnStopDecode called multiple times.");
-
- mState |= onStopDecode;
+ // We finished the decode, and thus have the decoded frames. Update the cache
+ // entry size to take this into account.
+ UpdateCacheEntrySize();
- if (NS_FAILED(aStatus) && !(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL)) {
- mImageStatus |= imgIRequest::STATUS_ERROR;
- }
-
- nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
- while (iter.HasMore()) {
- iter.GetNext()->OnStopDecode(GetResultFromImageStatus(mImageStatus), aStatusArg);
- }
+ // ImgContainer and everything below it is completely correct and
+ // bulletproof about its handling of decoder notifications.
+ // Unfortunately, here and above we have to make some gross and
+ // inappropriate use of things to get things to work without
+ // completely overhauling the decoder observer interface (this will,
+ // thankfully, happen in bug 505385). From imgRequest and above (for
+ // the time being), OnStopDecode is just a companion to OnStopRequest
+ // that signals success or failure of the _load_ (not the _decode_).
+ // As such, we ignore OnStopDecode notifications from the decoder and
+ // container and generate our own every time we send OnStopRequest.
+ // For more information, see bug 435296.
return NS_OK;
}
NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest,
PRBool aLastPart)
{
NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest");
return NS_OK;
}
+/* void onDiscard (in imgIRequest request); */
+NS_IMETHODIMP imgRequest::OnDiscard(imgIRequest *aRequest)
+{
+ // Clear the state bits we no longer deserve.
+ PRUint32 stateBitsToClear = stateDecodeStarted;
+ mState &= ~stateBitsToClear;
+
+ // Clear the status bits we no longer deserve.
+ PRUint32 statusBitsToClear = imgIRequest::STATUS_FRAME_COMPLETE;
+ mImageStatus &= ~statusBitsToClear;
+
+ // Update the cache entry size, since we just got rid of frame data
+ UpdateCacheEntrySize();
+
+ nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+ while (iter.HasMore()) {
+ iter.GetNext()->OnDiscard();
+ }
+
+ return NS_OK;
+
+}
+
/** nsIRequestObserver methods **/
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
{
nsresult rv;
LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
- NS_ASSERTION(!mDecoder, "imgRequest::OnStartRequest -- we already have a decoder");
-
+ // Figure out if we're multipart
nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
if (mpchan)
mIsMultiPartChannel = PR_TRUE;
+ // If we're not multipart, we shouldn't have an image yet
+ NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage,
+ "Already have an image for non-multipart request");
+
+ // If we're multipart and have an image, fix things up for another round
+ if (mIsMultiPartChannel && mImage) {
+
+ // Inform the container that we have new source data
+ mImage->NewSourceData();
+
+ // Clear any status and state bits indicating load/decode
+ mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL;
+ mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE;
+ mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE;
+ mState &= ~stateRequestStarted;
+ mState &= ~stateDecodeStarted;
+ mState &= ~stateRequestStopped;
+ }
+
/*
* If mRequest is null here, then we need to set it so that we'll be able to
* cancel it if our Cancel() method is called. Note that this can only
* happen for multipart channels. We could simply not null out mRequest for
* non-last parts, if GetIsLastPart() were reliable, but it's not. See
* https://bugzilla.mozilla.org/show_bug.cgi?id=339610
*/
if (!mRequest) {
NS_ASSERTION(mpchan,
"We should have an mRequest here unless we're multipart");
nsCOMPtr<nsIChannel> chan;
mpchan->GetBaseChannel(getter_AddRefs(chan));
mRequest = chan;
}
- /* set our state variables to their initial values, but advance mState
- to onStartRequest. */
- mImageStatus = imgIRequest::STATUS_NONE;
- mState = onStartRequest;
+ // The request has started
+ mState |= stateRequestStarted;
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
if (channel)
channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
/* set our loading flag to true */
mLoading = PR_TRUE;
@@ -797,24 +815,21 @@ NS_IMETHODIMP imgRequest::OnStartRequest
return NS_OK;
}
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
{
LOG_FUNC(gImgLog, "imgRequest::OnStopRequest");
- mState |= onStopRequest;
+ mState |= stateRequestStopped;
/* set our loading flag to false */
mLoading = PR_FALSE;
- /* set our processing flag to false */
- mProcessing = PR_FALSE;
-
mHadLastPart = PR_TRUE;
nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
if (mpchan) {
PRBool lastPart;
nsresult rv = mpchan->GetIsLastPart(&lastPart);
if (NS_SUCCEEDED(rv))
mHadLastPart = lastPart;
}
@@ -829,82 +844,96 @@ NS_IMETHODIMP imgRequest::OnStopRequest(
// stop holding a ref to the channel, since we don't need it anymore
if (mChannel) {
mChannel->SetNotificationCallbacks(mPrevChannelSink);
mPrevChannelSink = nsnull;
mChannel = nsnull;
}
- // If mImage is still null, we didn't properly load the image.
- if (NS_FAILED(status) || !mImage) {
- this->Cancel(status); // sets status, stops animations, removes from cache
- } else {
- mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
+ // Tell the image that it has all of the source data. Note that this can
+ // trigger a failure, since the image might be waiting for more non-optional
+ // data and this is the point where we break the news that it's not coming.
+ if (mImage) {
+
+ // Notify the image
+ nsresult rv = mImage->SourceDataComplete();
+
+ // If we got an error in the SourceDataComplete() call, we don't want to
+ // proceed as if nothing bad happened. However, we also want to give
+ // precedence to failure status codes from necko, since presumably
+ // they're more meaningful.
+ if (NS_FAILED(rv) && NS_SUCCEEDED(status))
+ status = rv;
}
- if (mDecoder) {
- mDecoder->Flush();
- mDecoder->Close();
- mDecoder = nsnull; // release the decoder so that it can rest peacefully ;)
+ // If the request went through, say we loaded the image, and update the
+ // cache entry size. Otherwise, cancel the request, which adds an error
+ // flag to mImageStatus.
+ if (NS_SUCCEEDED(status)) {
+
+ // Flag that we loaded the image
+ mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
+
+ // We update the cache entry size here because this is where we finish
+ // loading compressed source data, which is part of our size calculus.
+ UpdateCacheEntrySize();
+ }
+ else
+ this->Cancel(status); // sets status, stops animations, removes from cache
+
+ /* notify the kids */
+ nsTObserverArray<imgRequestProxy*>::ForwardIterator sdIter(mObservers);
+ while (sdIter.HasMore()) {
+ sdIter.GetNext()->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
}
- // if there was an error loading the image, (mState & onStopDecode) won't be true.
- // Send an onStopDecode message
- if (!(mState & onStopDecode)) {
- this->OnStopDecode(nsnull, status, nsnull);
- }
-
- /* notify the kids */
- nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
- while (iter.HasMore()) {
- iter.GetNext()->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
+ nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mObservers);
+ while (srIter.HasMore()) {
+ srIter.GetNext()->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
}
return NS_OK;
}
-/* prototype for this defined below */
+/* prototype for these defined below */
static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount);
-
/** nsIStreamListener methods **/
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
{
LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "count", count);
NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
mGotData = PR_TRUE;
+ nsresult rv;
- if (!mProcessing) {
+ if (!mImage) {
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");
- /* set our processing flag to true if this is the first OnDataAvailable() */
- mProcessing = PR_TRUE;
-
/* look at the first few bytes and see if we can tell what the data is from that
* since servers tend to lie. :(
*/
PRUint32 out;
inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);
#ifdef NS_DEBUG
/* NS_WARNING if the content type from the channel isn't the same if the sniffing */
#endif
if (mContentType.IsEmpty()) {
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
- nsresult rv = NS_ERROR_FAILURE;
+ rv = NS_ERROR_FAILURE;
if (chan) {
rv = chan->GetContentType(mContentType);
}
if (NS_FAILED(rv)) {
PR_LOG(gImgLog, PR_LOG_ERROR,
("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
this));
@@ -940,61 +969,89 @@ NS_IMETHODIMP imgRequest::OnDataAvailabl
if (contentDisposition) {
contentDisposition->SetData(disposition);
mProperties->Set("content-disposition", contentDisposition);
}
}
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
- nsCAutoString conid(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mContentType);
+ //
+ // Figure out if our container initialization flags
+ //
- mDecoder = do_CreateInstance(conid.get());
+ // We default to the static globals
+ PRBool isDiscardable = gDiscardable;
+ PRBool doDecodeOnDraw = gDecodeOnDraw;
- if (!mDecoder) {
- PR_LOG(gImgLog, PR_LOG_WARNING,
- ("[this=%p] imgRequest::OnDataAvailable -- Decoder not available\n", this));
+ // We want UI to be as snappy as possible and not to flicker. Disable discarding
+ // and decode-on-draw for chrome URLS
+ PRBool isChrome = PR_FALSE;
+ rv = mURI->SchemeIs("chrome", &isChrome);
+ if (NS_SUCCEEDED(rv) && isChrome)
+ isDiscardable = doDecodeOnDraw = PR_FALSE;
- // no image decoder for this mimetype :(
- this->Cancel(NS_IMAGELIB_ERROR_NO_DECODER);
+ // We don't want resources like the "loading" icon to be discardable or
+ // decode-on-draw either.
+ PRBool isResource = PR_FALSE;
+ rv = mURI->SchemeIs("resource", &isResource);
+ if (NS_SUCCEEDED(rv) && isResource)
+ isDiscardable = doDecodeOnDraw = PR_FALSE;
+
+ // For multipart/x-mixed-replace, we basically want a direct channel to the
+ // decoder. Disable both for this case as well.
+ if (mIsMultiPartChannel)
+ isDiscardable = doDecodeOnDraw = PR_FALSE;
- return NS_IMAGELIB_ERROR_NO_DECODER;
- }
+ // We have all the information we need
+ PRUint32 containerFlags = imgIContainer::INIT_FLAG_NONE;
+ if (isDiscardable)
+ containerFlags |= imgIContainer::INIT_FLAG_DISCARDABLE;
+ if (doDecodeOnDraw)
+ containerFlags |= imgIContainer::INIT_FLAG_DECODE_ON_DRAW;
+ if (mIsMultiPartChannel)
+ containerFlags |= imgIContainer::INIT_FLAG_MULTIPART;
- nsresult rv = mDecoder->Init(static_cast<imgILoad*>(this));
- if (NS_FAILED(rv)) {
- PR_LOG(gImgLog, PR_LOG_WARNING,
- ("[this=%p] imgRequest::OnDataAvailable -- mDecoder->Init failed\n", this));
+ // Create and initialize the imgContainer. This instantiates a decoder behind
+ // the scenes, so if we don't have a decoder for this mimetype we'll find out
+ // about it here.
+ mImage = do_CreateInstance("@mozilla.org/image/container;3");
+ if (!mImage) {
+ this->Cancel(NS_ERROR_OUT_OF_MEMORY);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ rv = mImage->Init(this, mContentType.get(), containerFlags);
+ if (NS_FAILED(rv)) { // Probably bad mimetype
- this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
+ // There's no reason to keep the image around. Save memory.
+ //
+ // XXXbholley - This is also here because I'm not sure we've found
+ // all the consumers who (incorrectly) check whether the container
+ // is null to determine things like size availability (they should
+ // be checking the image status instead).
+ mImage = nsnull;
+ this->Cancel(rv);
return NS_BINDING_ABORTED;
}
}
- if (!mDecoder) {
+ // WriteToContainer always consumes everything it gets
+ PRUint32 bytesRead;
+ rv = inStr->ReadSegments(imgContainer::WriteToContainer,
+ static_cast<void*>(mImage),
+ count, &bytesRead);
+ if (NS_FAILED(rv)) {
PR_LOG(gImgLog, PR_LOG_WARNING,
- ("[this=%p] imgRequest::OnDataAvailable -- no decoder\n", this));
-
- this->Cancel(NS_IMAGELIB_ERROR_NO_DECODER);
-
+ ("[this=%p] imgRequest::OnDataAvailable -- "
+ "copy to container failed\n", this));
+ this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
return NS_BINDING_ABORTED;
}
-
- PRUint32 wrote;
- nsresult rv = mDecoder->WriteFrom(inStr, count, &wrote);
-
- if (NS_FAILED(rv)) {
- PR_LOG(gImgLog, PR_LOG_WARNING,
- ("[this=%p] imgRequest::OnDataAvailable -- mDecoder->WriteFrom failed\n", this));
-
- this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
-
- return NS_BINDING_ABORTED;
- }
+ NS_ABORT_IF_FALSE(bytesRead == count, "WriteToContainer should consume everything!");
return NS_OK;
}
static NS_METHOD sniff_mimetype_callback(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
PRUint32 toOffset,
@@ -1031,16 +1088,17 @@ imgRequest::SniffMimeType(const char *bu
nsresult rv =
sniffers[i]->GetMIMETypeFromContent(nsnull, (const PRUint8 *) buf, len, mContentType);
if (NS_SUCCEEDED(rv) && !mContentType.IsEmpty()) {
return;
}
}
}
+
/** nsIInterfaceRequestor methods **/
NS_IMETHODIMP
imgRequest::GetInterface(const nsIID & aIID, void **aResult)
{
if (!mPrevChannelSink || aIID.Equals(NS_GET_IID(nsIChannelEventSink)))
return QueryInterface(aIID, aResult);
--- a/modules/libpr0n/src/imgRequest.h
+++ b/modules/libpr0n/src/imgRequest.h
@@ -17,16 +17,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
+ * Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -35,18 +36,16 @@
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef imgRequest_h__
#define imgRequest_h__
-#include "imgILoad.h"
-
#include "imgIContainer.h"
#include "imgIDecoder.h"
#include "imgIDecoderObserver.h"
#include "nsIChannelEventSink.h"
#include "nsIContentSniffer.h"
#include "nsIInterfaceRequestor.h"
#include "nsIRequest.h"
@@ -62,26 +61,23 @@
#include "nsWeakReference.h"
class imgCacheValidator;
class imgRequestProxy;
class imgCacheEntry;
enum {
- onStartRequest = PR_BIT(0),
- onStartDecode = PR_BIT(1),
- onStartContainer = PR_BIT(2),
- onStopContainer = PR_BIT(3),
- onStopDecode = PR_BIT(4),
- onStopRequest = PR_BIT(5)
+ stateRequestStarted = PR_BIT(0),
+ stateHasSize = PR_BIT(1),
+ stateDecodeStarted = PR_BIT(2),
+ stateRequestStopped = PR_BIT(4)
};
-class imgRequest : public imgILoad,
- public imgIDecoderObserver,
+class imgRequest : public imgIDecoderObserver,
public nsIStreamListener,
public nsSupportsWeakReference,
public nsIChannelEventSink,
public nsIInterfaceRequestor
{
public:
imgRequest();
virtual ~imgRequest();
@@ -110,16 +106,18 @@ public:
// being made...
PRBool IsReusable(void *aCacheId) { return !mLoading || (aCacheId == mCacheId); }
// Cancel, but also ensure that all work done in Init() is undone. Call this
// only when the channel has failed to open, and so calling Cancel() on it
// won't be sufficient.
void CancelAndAbort(nsresult aStatus);
+ nsresult GetImage(imgIContainer **aImage);
+
private:
friend class imgCacheEntry;
friend class imgRequestProxy;
friend class imgLoader;
friend class imgCacheValidator;
friend class imgCacheExpirationTracker;
inline void SetLoadId(void *aLoadId) {
@@ -164,34 +162,35 @@ private:
// Return whether we've seen some data at this point
PRBool HasTransferredData() const { return mGotData; }
// Set whether this request is stored in the cache. If it isn't, regardless
// of whether this request has a non-null mCacheEntry, this imgRequest won't
// try to update or modify the image cache.
void SetIsInCache(PRBool cacheable);
+ // Update the cache entry size based on the image container
+ void UpdateCacheEntrySize();
+
public:
- NS_DECL_IMGILOAD
NS_DECL_IMGIDECODEROBSERVER
NS_DECL_IMGICONTAINEROBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
private:
nsCOMPtr<nsIRequest> mRequest;
// The original URI we were loaded with.
nsCOMPtr<nsIURI> mURI;
// The URI we are keyed on in the cache.
nsCOMPtr<nsIURI> mKeyURI;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<imgIContainer> mImage;
- nsCOMPtr<imgIDecoder> mDecoder;
nsCOMPtr<nsIProperties> mProperties;
nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIInterfaceRequestor> mPrevChannelSink;
nsTObserverArray<imgRequestProxy*> mObservers;
PRUint32 mImageStatus;
@@ -205,15 +204,14 @@ private:
void *mLoadId;
PRTime mLoadTime;
imgCacheValidator *mValidator;
nsCategoryCache<nsIContentSniffer> mImageSniffers;
PRPackedBool mIsMultiPartChannel : 1;
PRPackedBool mLoading : 1;
- PRPackedBool mProcessing : 1;
PRPackedBool mHadLastPart : 1;
PRPackedBool mGotData : 1;
PRPackedBool mIsInCache : 1;
};
#endif
--- a/modules/libpr0n/src/imgRequestProxy.cpp
+++ b/modules/libpr0n/src/imgRequestProxy.cpp
@@ -59,27 +59,33 @@ NS_IMPL_ISUPPORTS4(imgRequestProxy, imgI
nsISupportsPriority, nsISecurityInfoProvider)
imgRequestProxy::imgRequestProxy() :
mOwner(nsnull),
mListener(nsnull),
mLoadFlags(nsIRequest::LOAD_NORMAL),
mCanceled(PR_FALSE),
mIsInLoadGroup(PR_FALSE),
- mListenerIsStrongRef(PR_FALSE)
+ mListenerIsStrongRef(PR_FALSE),
+ mShouldRequestDecode(PR_FALSE),
+ mLockHeld(PR_FALSE)
{
/* member initializers and constructor code */
}
imgRequestProxy::~imgRequestProxy()
{
/* destructor code */
NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!");
+ // Unlock the image if we're holding a lock on it
+ if (mLockHeld && mOwner)
+ UnlockImage();
+
// 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 (mOwner) {
if (!mCanceled) {
@@ -124,24 +130,42 @@ nsresult imgRequestProxy::Init(imgReques
return NS_OK;
}
nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
{
if (mCanceled)
return NS_OK;
+ // Were we decoded before?
+ PRBool wasDecoded = PR_FALSE;
+ if (mOwner->GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE)
+ wasDecoded = PR_TRUE;
+
+ // If we're holding a lock, unlock the old image
+ PRBool wasLocked = mLockHeld;
+ if (mLockHeld)
+ UnlockImage();
+
// Passing false to aNotify means that mListener will still get
// OnStopRequest, if needed.
mOwner->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER, PR_FALSE);
mOwner = aNewOwner;
mOwner->AddProxy(this);
+ // If we were decoded, request a decode on the new image
+ if (wasDecoded)
+ RequestDecode();
+
+ // If we were locked, apply the lock here
+ if (wasLocked)
+ LockImage();
+
return NS_OK;
}
void imgRequestProxy::AddToLoadGroup()
{
NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
if (!mIsInLoadGroup && mLoadGroup) {
@@ -237,16 +261,84 @@ NS_IMETHODIMP imgRequestProxy::CancelAnd
// OnStopRequest, if needed.
mOwner->RemoveProxy(this, aStatus, PR_FALSE);
NullOutListener();
return NS_OK;
}
+/* void requestDecode (); */
+NS_IMETHODIMP
+imgRequestProxy::RequestDecode()
+{
+ if (!mOwner)
+ return NS_ERROR_FAILURE;
+
+ // See if we can get the image
+ nsCOMPtr<imgIContainer> container;
+ nsresult rv = mOwner->GetImage(getter_AddRefs(container));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // If we've got the container, just forward along the request
+ if (container)
+ return container->RequestDecode();
+
+ // Otherwise, flag that we should do it when the container is ready
+ mShouldRequestDecode = PR_TRUE;
+ return NS_OK;
+}
+
+/* void lockImage (); */
+NS_IMETHODIMP
+imgRequestProxy::LockImage()
+{
+ if (!mOwner)
+ return NS_ERROR_FAILURE;
+
+ // Flag that we're holding a lock
+ NS_ABORT_IF_FALSE(!mLockHeld, "Only call lockImage once per imgIRequest!");
+ mLockHeld = PR_TRUE;
+
+ // If we've got the container, forward along the request. If we don't, well
+ // do it in OnStartContainer.
+ nsCOMPtr<imgIContainer> container;
+ nsresult rv = mOwner->GetImage(getter_AddRefs(container));
+ if (NS_FAILED(rv))
+ return rv;
+ if (container)
+ return container->LockImage();
+
+ return NS_OK;
+}
+
+/* void unlockImage (); */
+NS_IMETHODIMP
+imgRequestProxy::UnlockImage()
+{
+ if (!mOwner)
+ return NS_ERROR_FAILURE;
+
+ // Flag that we're not holding a lock
+ NS_ABORT_IF_FALSE(mLockHeld, "calling unlock but not locked!");
+ mLockHeld = PR_FALSE;
+
+ // If we've got the container, forward along the request. If we don't, it
+ // doesn't matter.
+ nsCOMPtr<imgIContainer> container;
+ nsresult rv = mOwner->GetImage(getter_AddRefs(container));
+ if (NS_FAILED(rv))
+ return rv;
+ if (container)
+ return container->UnlockImage();
+
+ return NS_OK;
+}
+
/* void suspend (); */
NS_IMETHODIMP imgRequestProxy::Suspend()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void resume (); */
NS_IMETHODIMP imgRequestProxy::Resume()
@@ -453,16 +545,26 @@ void imgRequestProxy::OnStartContainer(i
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnStartContainer");
if (mListener && !mCanceled) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStartContainer(this, image);
}
+
+ // Request a decode if we said we should
+ if (mShouldRequestDecode) {
+ image->RequestDecode();
+ mShouldRequestDecode = PR_FALSE;
+ }
+
+ // Lock if we said we should
+ if (mLockHeld)
+ image->LockImage();
}
void imgRequestProxy::OnStartFrame(PRUint32 frame)
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnStartFrame");
if (mListener && !mCanceled) {
// Hold a ref to the listener while we call it, just in case.
@@ -510,16 +612,28 @@ void imgRequestProxy::OnStopDecode(nsres
if (mListener && !mCanceled) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStopDecode(this, status, statusArg);
}
}
+void imgRequestProxy::OnDiscard()
+{
+ LOG_FUNC(gImgLog, "imgRequestProxy::OnDiscard");
+
+ if (mListener && !mCanceled) {
+ // Hold a ref to the listener while we call it, just in case.
+ nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
+ mListener->OnDiscard(this);
+ }
+}
+
+
void imgRequestProxy::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
#ifdef PR_LOGGING
nsCAutoString name;
GetName(name);
LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStartRequest", "name", name.get());
--- a/modules/libpr0n/src/imgRequestProxy.h
+++ b/modules/libpr0n/src/imgRequestProxy.h
@@ -111,16 +111,17 @@ protected:
/* non-virtual imgIDecoderObserver methods */
void OnStartDecode ();
void OnStartContainer(imgIContainer *aContainer);
void OnStartFrame (PRUint32 aFrame);
void OnDataAvailable (PRBool aCurrentFrame, const nsIntRect * aRect);
void OnStopFrame (PRUint32 aFrame);
void OnStopContainer (imgIContainer *aContainer);
void OnStopDecode (nsresult status, const PRUnichar *statusArg);
+ void OnDiscard ();
/* non-virtual imgIContainerObserver methods */
void FrameChanged(imgIContainer *aContainer, nsIntRect * aDirtyRect);
/* non-virtual nsIRequestObserver (plus some) methods */
void OnStartRequest(nsIRequest *request, nsISupports *ctxt);
void OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult statusCode, PRBool aLastPart);
@@ -150,9 +151,11 @@ private:
// first OnStopRequest.
imgIDecoderObserver* mListener;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsLoadFlags mLoadFlags;
PRPackedBool mCanceled;
PRPackedBool mIsInLoadGroup;
PRPackedBool mListenerIsStrongRef;
+ PRPackedBool mShouldRequestDecode;
+ PRPackedBool mLockHeld;
};
--- a/modules/libpr0n/src/imgTools.cpp
+++ b/modules/libpr0n/src/imgTools.cpp
@@ -36,153 +36,28 @@
*
* ***** END LICENSE BLOCK ***** */
#include "imgTools.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "ImageErrors.h"
#include "imgIContainer.h"
-#include "imgILoad.h"
#include "imgIDecoder.h"
#include "imgIEncoder.h"
#include "imgIDecoderObserver.h"
#include "imgIContainerObserver.h"
#include "gfxContext.h"
#include "nsStringStream.h"
#include "nsComponentManagerUtils.h"
#include "nsWeakReference.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsStreamUtils.h"
#include "nsNetUtil.h"
-
-
-/* ========== Utility classes ========== */
-
-
-class HelperLoader : public imgILoad,
- public imgIDecoderObserver,
- public nsSupportsWeakReference
-{
- public:
- NS_DECL_ISUPPORTS
- NS_DECL_IMGILOAD
- NS_DECL_IMGIDECODEROBSERVER
- NS_DECL_IMGICONTAINEROBSERVER
- HelperLoader(void);
-
- private:
- nsCOMPtr<imgIContainer> mContainer;
-};
-
-NS_IMPL_ISUPPORTS4 (HelperLoader, imgILoad, imgIDecoderObserver, imgIContainerObserver, nsISupportsWeakReference)
-
-HelperLoader::HelperLoader (void)
-{
-}
-
-/* Implement imgILoad::image getter */
-NS_IMETHODIMP
-HelperLoader::GetImage(imgIContainer **aImage)
-{
- *aImage = mContainer;
- NS_IF_ADDREF (*aImage);
- return NS_OK;
-}
-
-/* Implement imgILoad::image setter */
-NS_IMETHODIMP
-HelperLoader::SetImage(imgIContainer *aImage)
-{
- mContainer = aImage;
- return NS_OK;
-}
-
-/* Implement imgILoad::isMultiPartChannel getter */
-NS_IMETHODIMP
-HelperLoader::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel)
-{
- *aIsMultiPartChannel = PR_FALSE;
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStartRequest() */
-NS_IMETHODIMP
-HelperLoader::OnStartRequest(imgIRequest *aRequest)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStartDecode() */
-NS_IMETHODIMP
-HelperLoader::OnStartDecode(imgIRequest *aRequest)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStartContainer() */
-NS_IMETHODIMP
-HelperLoader::OnStartContainer(imgIRequest *aRequest, imgIContainer
-*aContainer)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStartFrame() */
-NS_IMETHODIMP
-HelperLoader::OnStartFrame(imgIRequest *aRequest, PRUint32 aFrame)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onDataAvailable() */
-NS_IMETHODIMP
-HelperLoader::OnDataAvailable(imgIRequest *aRequest, PRBool aCurrentFrame, const nsIntRect * aRect)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStopFrame() */
-NS_IMETHODIMP
-HelperLoader::OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStopContainer() */
-NS_IMETHODIMP
-HelperLoader::OnStopContainer(imgIRequest *aRequest, imgIContainer
-*aContainer)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStopDecode() */
-NS_IMETHODIMP
-HelperLoader::OnStopDecode(imgIRequest *aRequest, nsresult status, const
-PRUnichar *statusArg)
-{
- return NS_OK;
-}
-
-/* Implement imgIDecoderObserver::onStopRequest() */
-NS_IMETHODIMP
-HelperLoader::OnStopRequest(imgIRequest *aRequest, PRBool aIsLastPart)
-{
- return NS_OK;
-}
-
-/* implement imgIContainerObserver::frameChanged() */
-NS_IMETHODIMP
-HelperLoader::FrameChanged(imgIContainer *aContainer, nsIntRect * aDirtyRect)
-{
- return NS_OK;
-}
-
-
+#include "imgContainer.h"
/* ========== imgITools implementation ========== */
NS_IMPL_ISUPPORTS1(imgTools, imgITools)
imgTools::imgTools()
@@ -197,62 +72,58 @@ imgTools::~imgTools()
NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr,
const nsACString& aMimeType,
imgIContainer **aContainer)
{
nsresult rv;
- // Get an image decoder for our media type
- nsCAutoString decoderCID(
- NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + aMimeType);
-
- nsCOMPtr<imgIDecoder> decoder = do_CreateInstance(decoderCID.get());
- if (!decoder)
- return NS_IMAGELIB_ERROR_NO_DECODER;
+ NS_ENSURE_ARG_POINTER(aInStr);
+ // If the caller didn't provide a container, create one
+ if (!*aContainer) {
+ NS_NEWXPCOM(*aContainer, imgContainer);
+ if (!*aContainer)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*aContainer);
+ }
- // Init the decoder, we use a small utility class here.
- nsCOMPtr<imgILoad> loader = new HelperLoader();
- if (!loader)
- return NS_ERROR_OUT_OF_MEMORY;
-
- // If caller provided an existing container, use it.
- if (*aContainer)
- loader->SetImage(*aContainer);
-
- rv = decoder->Init(loader);
+ // Initialize the container. If we're using the one from the caller, we
+ // require that it not be initialized
+ nsCString mimeType(aMimeType);
+ rv = (*aContainer)->Init(nsnull, mimeType.get(), imgIContainer::INIT_FLAG_NONE);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> inStream = aInStr;
if (!NS_InputStreamIsBuffered(aInStr)) {
nsCOMPtr<nsIInputStream> bufStream;
rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), aInStr, 1024);
if (NS_SUCCEEDED(rv))
inStream = bufStream;
}
+ // Figure out how much data we've been passed
PRUint32 length;
rv = inStream->Available(&length);
NS_ENSURE_SUCCESS(rv, rv);
- PRUint32 written;
- rv = decoder->WriteFrom(inStream, length, &written);
- NS_ENSURE_SUCCESS(rv, rv);
- if (written != length)
- NS_WARNING("decoder didn't eat all of its vegetables");
- rv = decoder->Flush();
- NS_ENSURE_SUCCESS(rv, rv);
- rv = decoder->Close();
+ // Send the source data to the container. WriteToContainer always
+ // consumes everything it gets.
+ PRUint32 bytesRead;
+ rv = inStream->ReadSegments(imgContainer::WriteToContainer,
+ static_cast<void*>(*aContainer),
+ length, &bytesRead);
NS_ENSURE_SUCCESS(rv, rv);
- // If caller didn't provide an existing container, return the new one.
- if (!*aContainer)
- loader->GetImage(aContainer);
+ // Let the container know we've sent all the data
+ rv = (*aContainer)->SourceDataComplete();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // All done
return NS_OK;
}
NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer,
const nsACString& aMimeType,
nsIInputStream **aStream)
{
@@ -285,17 +156,18 @@ NS_IMETHODIMP imgTools::EncodeScaledImag
NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
if (!encoder)
return NS_IMAGELIB_ERROR_NO_ENCODER;
// Use frame 0 from the image container.
nsRefPtr<gfxImageSurface> frame;
- rv = aContainer->CopyCurrentFrame(getter_AddRefs(frame));
+ rv = aContainer->CopyFrame(imgIContainer::FRAME_CURRENT, PR_TRUE,
+ getter_AddRefs(frame));
NS_ENSURE_SUCCESS(rv, rv);
if (!frame)
return NS_ERROR_NOT_AVAILABLE;
PRInt32 w = frame->Width(), h = frame->Height();
if (!w || !h)
return NS_ERROR_FAILURE;
--- a/modules/libpr0n/test/mochitest/Makefile.in
+++ b/modules/libpr0n/test/mochitest/Makefile.in
@@ -39,18 +39,19 @@ DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = modules/libpr0n/test/mochitest
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
-_TEST_FILES = test_bug399925.html \
- bug399925.gif \
+_TEST_FILES = imgutils.js \
+ test_bug399925.html \
+ bug399925.gif \
bug468160.sjs \
test_bug468160.html \
red.png \
test_bug466586.html \
big.png \
blue.png \
test_bug490949.html \
bug490949-iframe.html \
new file mode 100644
--- /dev/null
+++ b/modules/libpr0n/test/mochitest/imgutils.js
@@ -0,0 +1,92 @@
+// Helper file for shared image functionality
+//
+// Note that this is use by tests elsewhere in the source tree. When in doubt,
+// check mxr before removing or changing functionality.
+
+// Helper function to determine if the frame is decoded for a given image id
+function isFrameDecoded(id)
+{
+ return (getImageStatus(id) &
+ Components.interfaces.imgIRequest.STATUS_FRAME_COMPLETE)
+ ? true : false;
+}
+
+// Helper function to determine if the image is loaded for a given image id
+function isImageLoaded(id)
+{
+ return (getImageStatus(id) &
+ Components.interfaces.imgIRequest.STATUS_LOAD_COMPLETE)
+ ? true : false;
+}
+
+// Helper function to get the status flags of an image
+function getImageStatus(id)
+{
+ // Escalate
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+ // Get the image
+ var img = document.getElementById(id);
+
+ // QI the image to nsImageLoadingContent
+ img.QueryInterface(Components.interfaces.nsIImageLoadingContent);
+
+ // Get the request
+ var request = img.getRequest(Components.interfaces
+ .nsIImageLoadingContent
+ .CURRENT_REQUEST);
+
+ // Return the status
+ return request.imageStatus;
+}
+
+// Forces a synchronous decode of an image by drawing it to a canvas. Only
+// really meaningful if the image is fully loaded first
+function forceDecode(id)
+{
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+ // Get the image
+ var img = document.getElementById(id);
+
+ // Make a new canvas
+ var canvas = document.createElement("canvas");
+
+ // Draw the image to the canvas. This forces a synchronous decode
+ var ctx = canvas.getContext("2d");
+ ctx.drawImage(img, 0, 0);
+}
+
+
+// Functions to facilitate getting/setting the discard timer pref
+//
+// Don't forget to reset the pref to the original value!
+//
+// Null indicates no pref set
+
+const DISCARD_BRANCH_NAME = "image.cache.";
+const DISCARD_PREF_NAME = "discard_timer_ms";
+
+function setDiscardTimerPref(timeMS)
+{
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var prefService = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefService);
+ var branch = prefService.getBranch(DISCARD_BRANCH_NAME);
+ if (timeMS != null)
+ branch.setIntPref(DISCARD_PREF_NAME, timeMS);
+ else if (branch.prefHasUserValue(DISCARD_PREF_NAME))
+ branch.clearUserPref(DISCARD_PREF_NAME);
+}
+
+function getDiscardTimerPref()
+{
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var prefService = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefService);
+ var branch = prefService.getBranch(DISCARD_BRANCH_NAME);
+ if (branch.prefHasUserValue(DISCARD_PREF_NAME))
+ return branch.getIntPref("discard_timeout_ms");
+ else
+ return null;
+}
--- a/modules/libpr0n/test/reftest/apng/delaytest.html
+++ b/modules/libpr0n/test/reftest/apng/delaytest.html
@@ -1,24 +1,41 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<head>
<title>Delayed image reftest wrapper</title>
</head>
<body>
<img id="image1">
<script>
-// This loads a externally specified image, waits 100ms, and then triggers the
-// reftest snapshot. This allows the animation on the page to complete.
+// This loads a externally specified image, forces a draw (in case of
+// decode-on-draw), waits 100ms, and then triggers the reftest snapshot.
+// This allows the animation on the page to complete.
//
// Use as "delaytest.html?animation.png"
//
+
+// Get the image URL from our URL
+var imgURL = document.location.search.substr(1);
+
+// Load the image
+var img = document.images[0];
+img.src = imgURL;
+img.onload = forceDecode;
+
+function forceDecode() {
+
+ // We need to force drawing of the image in an invisible context
+ var canvas = document.createElement("canvas");
+ var ctx = canvas.getContext("2d");
+ ctx.drawImage(img, 0, 0);
+
+ // We've force the decode. start the timer to trigger the reftest
+ startTimer();
+}
+
function startTimer() {
const delay = 100;
setTimeout("document.documentElement.className = '';", delay);
}
-var imgURL = document.location.search.substr(1);
-var img = document.images[0];
-img.src = imgURL;
-img.onload = startTimer;
</script>
</body>
</html>
--- a/modules/libpr0n/test/unit/test_imgtools.js
+++ b/modules/libpr0n/test/unit/test_imgtools.js
@@ -377,34 +377,38 @@ testnum = 413512;
testdesc = "test decoding bad favicon (bug 413512)";
imgName = "bug413512.ico";
inMimeType = "image/x-icon";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 17759);
+var errsrc = "none";
try {
outParam = { value: null };
imgTools.decodeImageData(istream, inMimeType, outParam);
container = outParam.value;
// We should never hit this - decodeImageData throws an assertion because the
// image decoded doesn't have enough frames.
try {
istream = imgTools.encodeImage(container, "image/png");
} catch (e) {
err = e;
+ errsrc = "encode";
}
} catch (e) {
err = e;
+ errsrc = "decode";
}
-checkExpectedError(/NS_ERROR_ILLEGAL_VALUE/, err);
+do_check_eq(errsrc, "decode");
+checkExpectedError(/NS_ERROR_FAILURE/, err);
/* ========== end ========== */
} catch (e) {
throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;
}
};
--- a/toolkit/system/gnome/nsAlertsIconListener.cpp
+++ b/toolkit/system/gnome/nsAlertsIconListener.cpp
@@ -157,16 +157,22 @@ nsAlertsIconListener::OnStopRequest(imgI
if (mIconRequest) {
mIconRequest->Cancel(NS_BINDING_ABORTED);
mIconRequest = nsnull;
}
return NS_OK;
}
NS_IMETHODIMP
+nsAlertsIconListener::OnDiscard(imgIRequest *aRequest)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsAlertsIconListener::OnStopFrame(imgIRequest* aRequest,
PRUint32 aFrame)
{
if (aRequest != mIconRequest)
return NS_ERROR_FAILURE;
if (mLoadedFrame)
return NS_OK; // only use one frame
--- a/widget/src/cocoa/nsClipboard.mm
+++ b/widget/src/cocoa/nsClipboard.mm
@@ -427,17 +427,19 @@ nsClipboard::PasteboardDictFromTransfera
nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData));
if (!image) {
NS_WARNING("Image isn't an imgIContainer in transferable");
continue;
}
nsRefPtr<gfxImageSurface> currentFrame;
- if (NS_FAILED(image->CopyCurrentFrame(getter_AddRefs(currentFrame))))
+ if (NS_FAILED(image->CopyFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE,
+ getter_AddRefs(currentFrame))))
continue;
PRInt32 height = currentFrame->Height();
PRInt32 stride = currentFrame->Stride();
PRInt32 width = currentFrame->Width();
if ((stride % 4 != 0) || (height < 1) || (width < 1))
continue;
--- a/widget/src/cocoa/nsMenuItemIconX.mm
+++ b/widget/src/cocoa/nsMenuItemIconX.mm
@@ -301,16 +301,20 @@ nsMenuItemIconX::OnStartDecode(imgIReque
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuItemIconX::OnStartContainer(imgIRequest* aRequest,
imgIContainer* aContainer)
{
+ // Request a decode
+ NS_ABORT_IF_FALSE(aContainer, "who sent the notification then?");
+ aContainer->RequestDecode();
+
return NS_OK;
}
NS_IMETHODIMP
nsMenuItemIconX::OnStartFrame(imgIRequest* aRequest, PRUint32 aFrame)
{
return NS_OK;
}
@@ -339,17 +343,19 @@ nsMenuItemIconX::OnStopFrame(imgIRequest
if (!mNativeMenuItem) return NS_ERROR_FAILURE;
nsCOMPtr<imgIContainer> imageContainer;
aRequest->GetImage(getter_AddRefs(imageContainer));
if (!imageContainer)
return NS_ERROR_FAILURE;
nsRefPtr<gfxImageSurface> image;
- imageContainer->CopyCurrentFrame(getter_AddRefs(image));
+ imageContainer->CopyFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_NONE,
+ getter_AddRefs(image));
PRInt32 height = image->Height();
PRInt32 stride = image->Stride();
PRInt32 width = image->Width();
PRUint32 imageLength = ((stride * height) / 4);
if ((stride % 4 != 0) || (height < 1) || (width < 1))
return NS_ERROR_FAILURE;
@@ -473,8 +479,14 @@ nsMenuItemIconX::OnStopRequest(imgIReque
{
NS_ASSERTION(mIconRequest, "NULL mIconRequest! Multiple calls to OnStopRequest()?");
if (mIconRequest) {
mIconRequest->Cancel(NS_BINDING_ABORTED);
mIconRequest = nsnull;
}
return NS_OK;
}
+
+NS_IMETHODIMP
+nsMenuItemIconX::OnDiscard(imgIRequest* aRequest)
+{
+ return NS_OK;
+}
--- a/widget/src/gtk2/nsImageToPixbuf.cpp
+++ b/widget/src/gtk2/nsImageToPixbuf.cpp
@@ -64,17 +64,19 @@ nsImageToPixbuf::ConvertImageToPixbuf(im
{
return ImageToPixbuf(aImage);
}
GdkPixbuf*
nsImageToPixbuf::ImageToPixbuf(imgIContainer* aImage)
{
nsRefPtr<gfxImageSurface> frame;
- aImage->CopyCurrentFrame(getter_AddRefs(frame));
+ aImage->CopyFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE,
+ getter_AddRefs(frame));
return ImgSurfaceToPixbuf(frame, frame->Width(), frame->Height());
}
GdkPixbuf*
nsImageToPixbuf::ImgSurfaceToPixbuf(gfxImageSurface* aImgSurface, PRInt32 aWidth, PRInt32 aHeight)
{
GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, PR_TRUE, 8,
--- a/widget/src/os2/nsWindow.cpp
+++ b/widget/src/os2/nsWindow.cpp
@@ -1614,17 +1614,19 @@ NS_IMETHODIMP nsWindow::SetCursor(imgICo
// it will be destroyed when we create a new one or when the
// current window is destroyed
if (mCssCursorImg == aCursor && mCssCursorHPtr) {
WinSetPointer(HWND_DESKTOP, mCssCursorHPtr);
return NS_OK;
}
nsRefPtr<gfxImageSurface> frame;
- aCursor->CopyCurrentFrame(getter_AddRefs(frame));
+ aCursor->CopyFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE,
+ getter_AddRefs(frame));
if (!frame)
return NS_ERROR_NOT_AVAILABLE;
// if the image is ridiculously large, exit because
// it will be unrecognizable when shrunk to 32x32
PRInt32 width = frame->Width();
PRInt32 height = frame->Height();
if (width > 128 || height > 128)
--- a/widget/src/windows/nsImageClipboard.cpp
+++ b/widget/src/windows/nsImageClipboard.cpp
@@ -144,17 +144,19 @@ nsImageToClipboard::CalcSpanLength(PRUin
//
nsresult
nsImageToClipboard::CreateFromImage ( imgIContainer* inImage, HANDLE* outBitmap )
{
nsresult result = NS_OK;
*outBitmap = nsnull;
nsRefPtr<gfxImageSurface> frame;
- nsresult rv = inImage->CopyCurrentFrame(getter_AddRefs(frame));
+ nsresult rv = inImage->CopyFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE,
+ getter_AddRefs(frame));
if (NS_FAILED(rv))
return rv;
const PRUint32 imageSize = frame->GetDataSize();
const PRInt32 bitmapSize = sizeof(BITMAPINFOHEADER) + imageSize;
HGLOBAL glob = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, bitmapSize);
if (!glob) {
--- a/widget/src/windows/nsWindowGfx.cpp
+++ b/widget/src/windows/nsWindowGfx.cpp
@@ -687,17 +687,19 @@ nsresult nsWindowGfx::CreateIcon(imgICon
rv = aContainer->GetNumFrames(&nFrames);
NS_ENSURE_SUCCESS(rv, rv);
if (!nFrames)
return NS_ERROR_INVALID_ARG;
// Get the image data
nsRefPtr<gfxImageSurface> frame;
- aContainer->CopyCurrentFrame(getter_AddRefs(frame));
+ aContainer->CopyFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE,
+ getter_AddRefs(frame));
if (!frame)
return NS_ERROR_NOT_AVAILABLE;
PRUint8 *data = frame->Data();
PRInt32 width = frame->Width();
PRInt32 height = frame->Height();
--- a/widget/src/xpwidgets/nsBaseDragService.cpp
+++ b/widget/src/xpwidgets/nsBaseDragService.cpp
@@ -596,17 +596,18 @@ nsBaseDragService::DrawDragForImage(nsPr
*aSurface = surface;
NS_ADDREF(*aSurface);
if (aImageLoader) {
gfxRect outRect(0, 0, destSize.width, destSize.height);
gfxMatrix scale =
gfxMatrix().Scale(srcSize.width/outRect.Width(), srcSize.height/outRect.Height());
nsIntRect imgSize(0, 0, srcSize.width, srcSize.height);
- imgContainer->Draw(ctx, gfxPattern::FILTER_GOOD, scale, outRect, imgSize);
+ imgContainer->Draw(ctx, gfxPattern::FILTER_GOOD, scale, outRect, imgSize,
+ imgIContainer::FLAG_SYNC_DECODE);
return NS_OK;
} else {
return aCanvas->RenderContexts(ctx, gfxPattern::FILTER_GOOD);
}
}
void
nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,