Due to popular demand, broken images are now getting an icon and a sized-box if they have width and height and are in QuirksMode documents (see bug for pref to override). b=102281 r=kmcclusk sr=waterson

git-svn-id: svn://10.0.0.236/trunk@107568 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
attinasi%netscape.com
2001-11-07 05:02:42 +00:00
parent ab5246a768
commit 9e73b286fe
4 changed files with 602 additions and 100 deletions

View File

@@ -93,12 +93,15 @@ static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
#include "nsIScriptGlobalObject.h"
#include "nsIDOMWindow.h"
#include "nsIDOMDocument.h"
#include "nsCSSFrameConstructor.h"
#include "nsIPref.h"
#ifdef DEBUG
#undef NOISY_IMAGE_LOADING
#undef NOISY_ICON_LOADING
#else
#undef NOISY_IMAGE_LOADING
#undef NOISY_ICON_LOADING
#endif
@@ -108,13 +111,39 @@ static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
#define SUPPRESS 2
#define DEFAULT_SUPPRESS 3
// values for image loading icons
#define NS_ICON_LOADING_IMAGE (0)
#define NS_ICON_BROKEN_IMAGE (1)
// Default alignment value (so we can tell an unset value from a set value)
#define ALIGN_UNSET PRUint8(-1)
// static icon information: initialized in Init and de-initialized in Destroy
// - created if null, deleted if refcount goes to 0
nsImageFrame::IconLoad* nsImageFrame::mIconLoad = nsnull;
// test if the width and height are fixed, looking at the style data
static PRBool HaveFixedSize(const struct nsStylePosition& aStylePosition)
{
// check the width and height values in the reflow state's style struct
// - if width and height are specified as either coord or percentage, then
// the size of the image frame is constrained
nsStyleUnit widthUnit = aStylePosition.mWidth.GetUnit();
nsStyleUnit heightUnit = aStylePosition.mHeight.GetUnit();
return ((widthUnit == eStyleUnit_Coord ||
widthUnit == eStyleUnit_Percent) &&
(heightUnit == eStyleUnit_Coord ||
heightUnit == eStyleUnit_Percent));
}
// use the data in the reflow state to decide if the image has a constrained size
// (i.e. width and height that are based on the containing block size and not the image size)
// so we can avoid animated GIF related reflows
static void HaveFixedSize(const nsHTMLReflowState& aReflowState, PRPackedBool& aConstrainedSize);
inline PRBool HaveFixedSize(const nsHTMLReflowState& aReflowState)
{
NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition");
return HaveFixedSize(*(aReflowState.mStylePosition));
}
nsresult
NS_NewImageFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
@@ -236,6 +265,21 @@ nsImageFrame::Destroy(nsIPresContext* aPresContext)
mListener = nsnull;
// check / cleanup the IconLoads singleton
if (mIconLoad) {
#ifdef NOISY_ICON_LOADING
printf( "Releasing IconLoad (%p)\n", this);
#endif
if (mIconLoad->Release()) {
#ifdef NOISY_ICON_LOADING
printf( "Deleting IconLoad (%p)\n", this);
#endif
delete mIconLoad;
mIconLoad = nsnull;
}
}
return nsSplittableFrame::Destroy(aPresContext);
}
@@ -269,6 +313,8 @@ nsImageFrame::Init(nsIPresContext* aPresContext,
mInitialLoadCompleted = PR_FALSE;
mCanSendLoadEvent = PR_TRUE;
LoadIcons(aPresContext);
mLoads[0].mRequest = do_CreateInstance("@mozilla.org/image/request;1");
rv = LoadImage(src, aPresContext, mLoads[0].mRequest); // if the image was found in the cache,
// it is possible that LoadImage will result in
@@ -289,6 +335,11 @@ NS_IMETHODIMP nsImageFrame::OnStartContainer(imgIRequest *aRequest, nsIPresConte
mInitialLoadCompleted = PR_TRUE;
// handle iconLoads first...
if (HandleIconLoads(aRequest, PR_FALSE)) {
return NS_OK;
}
int whichLoad = GetImageLoad(aRequest);
if (whichLoad == -1) return NS_ERROR_FAILURE;
struct ImageLoad *load= &mLoads[whichLoad];
@@ -350,6 +401,14 @@ NS_IMETHODIMP nsImageFrame::OnDataAvailable(imgIRequest *aRequest, nsIPresContex
if (!aRect)
return NS_ERROR_NULL_POINTER;
// handle iconLoads first...
if (HandleIconLoads(aRequest, PR_FALSE)) {
if (!aRect->IsEmpty()) {
Invalidate(aPresContext, *aRect, PR_FALSE);
}
return NS_OK;
}
int whichLoad = GetImageLoad(aRequest);
if (whichLoad == -1) return NS_ERROR_FAILURE;
if (whichLoad != 0) return NS_OK;
@@ -533,6 +592,11 @@ NS_IMETHODIMP nsImageFrame::OnStopDecode(imgIRequest *aRequest, nsIPresContext *
// check to see if an image error occurred
PRBool imageFailedToLoad = PR_FALSE;
// handle iconLoads first...
if (HandleIconLoads(aRequest, NS_SUCCEEDED(aStatus))) {
return NS_OK;
}
int whichLoad = GetImageLoad(aRequest);
if (whichLoad == -1) return NS_ERROR_FAILURE;
@@ -564,7 +628,9 @@ NS_IMETHODIMP nsImageFrame::OnStopDecode(imgIRequest *aRequest, nsIPresContext *
nsSize s;
GetSize(s);
nsRect r(0,0, s.width, s.height);
Invalidate(aPresContext, r, PR_FALSE);
if (!r.IsEmpty()) {
Invalidate(aPresContext, r, PR_FALSE);
}
}
} else {
@@ -579,14 +645,42 @@ NS_IMETHODIMP nsImageFrame::OnStopDecode(imgIRequest *aRequest, nsIPresContext *
}
// if src failed then notify the PresShell
// if src failed to load, determine how to handle it:
// - either render the ALT text in this frame, or let the presShell handle it
if (imageFailedToLoad && presShell) {
if (mFailureReplace) {
// first check for image map
nsAutoString usemap;
mContent->GetAttr(kNameSpaceID_HTML, nsHTMLAtoms::usemap, usemap);
// We failed to load the image. Notify the pres shell if we aren't an image map
if (usemap.IsEmpty()) {
presShell->CantRenderReplacedElement(aPresContext, this);
// check if we want to honor the ALT text in the IMG frame, or let the preShell make it into inline text
// - if QuirksMode, and the IMG has a size, then render the ALT text in the ING frame
// UNLESS there is a pref set to force inline alt text
PRBool useSizedBox = PR_FALSE;
PRBool prefForceInlineAltText = mIconLoad ? mIconLoad->mPrefForceInlineAltText : PR_FALSE;
// check if we have fixed size
const nsStylePosition* stylePosition =
(const nsStylePosition*)mStyleContext->GetStyleData(eStyleStruct_Position);
NS_ASSERTION(stylePosition, "null style position: frame is corrupted");
// check for quirks mode
nsCompatibility mode;
aPresContext->GetCompatibilityMode(&mode);
// wrap it all up
useSizedBox = !prefForceInlineAltText &&
HaveFixedSize(*stylePosition) &&
mode == eCompatibility_NavQuirks;
if (!useSizedBox) {
// let the presShell handle converting this into the inline alt text frame
presShell->CantRenderReplacedElement(aPresContext, this);
} else {
// we are handling it
// invalidate the icon area (it may change states)
InvalidateIcon(aPresContext);
}
}
}
mFailureReplace = PR_TRUE;
@@ -627,8 +721,9 @@ NS_IMETHODIMP nsImageFrame::FrameChanged(imgIContainer *aContainer, nsIPresConte
r.height = NSIntPixelsToTwips(r.height, p2t);
mLoads[0].mTransform.TransformCoord(&r.x, &r.y, &r.width, &r.height);
Invalidate(aPresContext, r, PR_FALSE);
if (!r.IsEmpty()) {
Invalidate(aPresContext, r, PR_FALSE);
}
}
return NS_OK;
}
@@ -816,7 +911,7 @@ nsImageFrame::Reflow(nsIPresContext* aPresContext,
aStatus = NS_FRAME_COMPLETE;
// see if we have a frozen size (i.e. a fixed width and height)
HaveFixedSize(aReflowState, mSizeConstrained);
mSizeConstrained = HaveFixedSize(aReflowState);
if (aReflowState.reason == eReflowReason_Initial)
mGotInitialReflow = PR_TRUE;
@@ -1005,6 +1100,10 @@ struct nsRecessedBorder : public nsStyleBorder {
}
};
#define ICON_SIZE (16)
#define ICON_PADDING (6)
#define ALT_BORDER_WIDTH (1)
void
nsImageFrame::DisplayAltFeedback(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@@ -1018,7 +1117,7 @@ nsImageFrame::DisplayAltFeedback(nsIPresContext* aPresContext,
float p2t;
nscoord borderEdgeWidth;
aPresContext->GetScaledPixelsToTwips(&p2t);
borderEdgeWidth = NSIntPixelsToTwips(1, p2t);
borderEdgeWidth = NSIntPixelsToTwips(ALT_BORDER_WIDTH, p2t);
// Make sure we have enough room to actually render the border within
// our frame bounds
@@ -1033,7 +1132,8 @@ nsImageFrame::DisplayAltFeedback(nsIPresContext* aPresContext,
// Adjust the inner rect to account for the one pixel recessed border,
// and a six pixel padding on each edge
inner.Deflate(NSIntPixelsToTwips(7, p2t), NSIntPixelsToTwips(7, p2t));
inner.Deflate(NSIntPixelsToTwips(ICON_PADDING+ALT_BORDER_WIDTH, p2t),
NSIntPixelsToTwips(ICON_PADDING+ALT_BORDER_WIDTH, p2t));
if (inner.IsEmpty()) {
return;
}
@@ -1043,34 +1143,61 @@ nsImageFrame::DisplayAltFeedback(nsIPresContext* aPresContext,
aRenderingContext.PushState();
aRenderingContext.SetClipRect(inner, nsClipCombine_kIntersect, clipState);
// Display the icon
#ifdef USE_IMG2
// XXX
#else
nsIDeviceContext* dc;
aRenderingContext.GetDeviceContext(dc);
nsIImage* icon;
PRInt32 size = NSIntPixelsToTwips(ICON_SIZE, p2t);
if (NS_SUCCEEDED(dc->LoadIconImage(aIconId, icon)) && icon) {
aRenderingContext.DrawImage(icon, inner.x, inner.y);
PRBool iconUsed = PR_FALSE;
// Reduce the inner rect by the width of the icon, and leave an
// additional six pixels padding
PRInt32 iconWidth = NSIntPixelsToTwips(icon->GetWidth() + 6, p2t);
inner.x += iconWidth;
inner.width -= iconWidth;
// see if the icon images are present...
if (mIconLoad && mIconLoad->mIconsLoaded) {
// pick the correct image
NS_ASSERTION( aIconId == NS_ICON_LOADING_IMAGE ||
aIconId == NS_ICON_BROKEN_IMAGE, "Invalid Icon ID in DisplayAltFeedback");
NS_RELEASE(icon);
nsCOMPtr<imgIContainer> imgCon;
if (mIconLoad->mIconLoads[aIconId].mRequest) {
mIconLoad->mIconLoads[aIconId].mRequest->GetImage(getter_AddRefs(imgCon));
}
if (imgCon) {
// draw it
nsPoint p(inner.x, inner.y);
nsRect r(0,0,size,size);
r.x -= mBorderPadding.left;
r.y -= mBorderPadding.top;
aRenderingContext.DrawImage(imgCon, &r, &p);
iconUsed = PR_TRUE;
}
}
NS_RELEASE(dc);
#endif
// if we could not draw the image, then just draw some grafitti
if (!iconUsed) {
nscolor oldColor;
aRenderingContext.DrawRect(0,0,size,size);
aRenderingContext.GetColor(oldColor);
aRenderingContext.SetColor( aIconId == NS_ICON_BROKEN_IMAGE ? NS_RGB(0xFF,0,0) : NS_RGB(0,0xFF,0));
aRenderingContext.FillEllipse(NS_STATIC_CAST(int,size/2),NS_STATIC_CAST(int,size/2),
NS_STATIC_CAST(int,(size/2)-(2*p2t)),NS_STATIC_CAST(int,(size/2)-(2*p2t)));
aRenderingContext.SetColor(oldColor);
}
// Reduce the inner rect by the width of the icon, and leave an
// additional ICON_PADDING pixels for padding
PRInt32 iconWidth = NSIntPixelsToTwips(ICON_SIZE + ICON_PADDING, p2t);
inner.x += iconWidth;
inner.width -= iconWidth;
// If there's still room, display the alt-text
if (!inner.IsEmpty()) {
nsAutoString altText;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_HTML, nsHTMLAtoms::alt, altText)) {
DisplayAltText(aPresContext, aRenderingContext, altText, inner);
nsCOMPtr<nsIContent> content;
nsCOMPtr<nsIAtom> tag;
nsAutoString altText;
GetContent(getter_AddRefs(content));
if (content) {
content->GetTag(*getter_AddRefs(tag));
if (tag) {
nsCSSFrameConstructor::GetAlternateTextFor(content, tag, altText);
DisplayAltText(aPresContext, aRenderingContext, altText, inner);
}
}
}
@@ -1132,14 +1259,16 @@ nsImageFrame::Paint(nsIPresContext* aPresContext,
if (loadStatus & imgIRequest::STATUS_ERROR || !imgCon) {
// No image yet, or image load failed. Draw the alt-text and an icon
// indicating the status
#ifndef SUPPRESS_LOADING_ICON
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
#else
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer &&
!mInitialLoadCompleted) {
DisplayAltFeedback(aPresContext, aRenderingContext, 0);
#ifndef USE_IMG2
mInitialLoadCompleted) {
#endif
DisplayAltFeedback(aPresContext, aRenderingContext,
(loadStatus & imgIRequest::STATUS_ERROR)
? NS_ICON_BROKEN_IMAGE
: NS_ICON_LOADING_IMAGE);
#endif
}
}
else {
@@ -1711,21 +1840,6 @@ nsImageFrame::IsImageComplete(PRBool* aComplete)
return NS_OK;
}
void HaveFixedSize(const nsHTMLReflowState& aReflowState, PRPackedBool& aConstrainedSize)
{
// check the width and height values in the reflow state's style struct
// - if width and height are specified as either coord or percentage, then
// the size of the image frame is constrained
nsStyleUnit widthUnit = aReflowState.mStylePosition->mWidth.GetUnit();
nsStyleUnit heightUnit = aReflowState.mStylePosition->mHeight.GetUnit();
aConstrainedSize =
((widthUnit == eStyleUnit_Coord ||
widthUnit == eStyleUnit_Percent) &&
(heightUnit == eStyleUnit_Coord ||
heightUnit == eStyleUnit_Percent));
}
nsresult
nsImageFrame::LoadImage(const nsAReadableString& aSpec, nsIPresContext *aPresContext, imgIRequest *aRequest)
{
@@ -1920,7 +2034,119 @@ nsImageFrame::CanLoadImage(nsIURI *aURI)
return shouldLoad;
}
nsresult nsImageFrame::LoadIcons(nsIPresContext *aPresContext)
{
// XXX
// XXX: change these icons!
// XXX
nsAutoString loadingSrc(NS_LITERAL_STRING("chrome://editor/content/images/undecided_n.gif"));
nsAutoString brokenSrc(NS_LITERAL_STRING("chrome://editor/content/images/tongue_n.gif"));
nsresult rv = NS_OK;
PRBool doLoad = PR_FALSE; // only load icons once...
// see if the first instance and we need to create the icon loader
if (!mIconLoad) {
#ifdef NOISY_ICON_LOADING
printf( "Allocating IconLoad (%p)\n", this);
#endif
mIconLoad = new IconLoad(aPresContext);
if (!mIconLoad)
return NS_ERROR_OUT_OF_MEMORY;
doLoad = PR_TRUE;
}
// always addref
mIconLoad->AddRef();
#ifdef NOISY_ICON_LOADING
printf( "IconLoad AddRef'd(%p)\n", this);
#endif
// if already loaded, we are done
if (mIconLoad->mIconsLoaded) return rv;
if (doLoad) {
// create a loader and load the images
mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest = do_CreateInstance("@mozilla.org/image/request;1");
if (mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest) {
#ifdef NOISY_ICON_LOADING
printf( "Loading request %p\n", mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest.get() );
#endif
rv = LoadImage(loadingSrc, aPresContext, mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest);
if (NS_SUCCEEDED(rv)) {
mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest = do_CreateInstance("@mozilla.org/image/request;1");
if (mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest) {
#ifdef NOISY_ICON_LOADING
printf( "Loading request %p\n", mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest.get() );
#endif
rv = LoadImage(brokenSrc, aPresContext, mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest);
// ImageLoader will callback into OnStartContainer, which will handle the mIconsLoaded flag
}
}
}
}
return rv;
}
PRBool nsImageFrame::HandleIconLoads(imgIRequest* aRequest, PRBool aLoaded)
{
PRBool result = PR_FALSE;
if( mIconLoad ) {
// check which image it is
if (aRequest == mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest.get()) {
result = PR_TRUE;
} else if (aRequest == mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest.get()) {
result = PR_TRUE;
}
if (result) {
mIconLoad->mIconsLoaded = aLoaded;
}
#ifdef NOISY_ICON_LOADING
if (mIconLoad->mIconsLoaded && result) {
printf( "Icons Loaded: request for %s\n", aRequest == mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest ?
"NS_ICON_LOADING_IMAGE" : "NS_ICON_BROKEN_IMAGE" );
}
#endif
}
#ifdef NOISY_ICON_LOADING
printf( "HandleIconLoads returned %s (%p)\n", result ? "TRUE" : "FALSE", this);
#endif
return result;
}
void nsImageFrame::InvalidateIcon(nsIPresContext *aPresContext)
{
NS_ASSERTION(aPresContext, "null presContext in InvalidateIcon");
float p2t;
aPresContext->GetScaledPixelsToTwips(&p2t);
nsRect inner;
GetInnerArea(aPresContext, inner);
nsRect rect(inner.x,
inner.y,
NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING, p2t),
NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING, p2t));
Invalidate(aPresContext, rect, PR_FALSE);
}
void nsImageFrame::IconLoad::GetAltModePref(nsIPresContext *aPresContext)
{
NS_ASSERTION(aPresContext, "null presContext is not allowed in GetAltModePref");
// NOTE: the presContext could be used to fetch a cached pref if needed, but is not for now
nsCOMPtr<nsIPref> prefs = do_GetService(NS_PREF_CONTRACTID);
if (prefs) {
PRBool boolPref;
if (NS_SUCCEEDED(prefs->GetBoolPref("browser.display.force_inline_alttext", &boolPref))) {
mPrefForceInlineAltText = boolPref;
} else {
mPrefForceInlineAltText = PR_FALSE;
}
}
}
#ifdef DEBUG
NS_IMETHODIMP

View File

@@ -250,6 +250,31 @@ private:
nsMargin mBorderPadding;
PRUint32 mNaturalImageWidth,
mNaturalImageHeight;
/* loading / broken image icon support */
// LoadIcons: initiate the loading of the static icons used to show loading / broken images
nsresult LoadIcons(nsIPresContext *aPresContext);
// HandleIconLoads: See if the request is for an Icon load. If it is, handle it and return TRUE
// otherwise, return FALSE (aCompleted is an input arg telling the routine if the request has completed)
PRBool HandleIconLoads(imgIRequest* aRequest, PRBool aCompleted);
void InvalidateIcon(nsIPresContext *aPresContext);
class IconLoad {
// private class that wraps the data and logic needed for
// broken image and loading image icons
public:
IconLoad(nsIPresContext *aPresContext):mRefCount(0),mIconsLoaded(PR_FALSE) { GetAltModePref(aPresContext); }
void AddRef(void) { ++mRefCount; }
PRBool Release(void) { return --mRefCount == 0; }
void GetAltModePref(nsIPresContext *aPresContext);
PRUint32 mRefCount;
struct ImageLoad mIconLoads[2]; // 0 is for the 'loading' icon, 1 is for the 'broken' icon
PRPackedBool mIconsLoaded;
PRPackedBool mPrefForceInlineAltText;
};
static IconLoad* mIconLoad; // singleton patern: one LoadIcons instance is used
};
#endif /* nsImageFrame_h___ */

View File

@@ -93,12 +93,15 @@ static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
#include "nsIScriptGlobalObject.h"
#include "nsIDOMWindow.h"
#include "nsIDOMDocument.h"
#include "nsCSSFrameConstructor.h"
#include "nsIPref.h"
#ifdef DEBUG
#undef NOISY_IMAGE_LOADING
#undef NOISY_ICON_LOADING
#else
#undef NOISY_IMAGE_LOADING
#undef NOISY_ICON_LOADING
#endif
@@ -108,13 +111,39 @@ static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
#define SUPPRESS 2
#define DEFAULT_SUPPRESS 3
// values for image loading icons
#define NS_ICON_LOADING_IMAGE (0)
#define NS_ICON_BROKEN_IMAGE (1)
// Default alignment value (so we can tell an unset value from a set value)
#define ALIGN_UNSET PRUint8(-1)
// static icon information: initialized in Init and de-initialized in Destroy
// - created if null, deleted if refcount goes to 0
nsImageFrame::IconLoad* nsImageFrame::mIconLoad = nsnull;
// test if the width and height are fixed, looking at the style data
static PRBool HaveFixedSize(const struct nsStylePosition& aStylePosition)
{
// check the width and height values in the reflow state's style struct
// - if width and height are specified as either coord or percentage, then
// the size of the image frame is constrained
nsStyleUnit widthUnit = aStylePosition.mWidth.GetUnit();
nsStyleUnit heightUnit = aStylePosition.mHeight.GetUnit();
return ((widthUnit == eStyleUnit_Coord ||
widthUnit == eStyleUnit_Percent) &&
(heightUnit == eStyleUnit_Coord ||
heightUnit == eStyleUnit_Percent));
}
// use the data in the reflow state to decide if the image has a constrained size
// (i.e. width and height that are based on the containing block size and not the image size)
// so we can avoid animated GIF related reflows
static void HaveFixedSize(const nsHTMLReflowState& aReflowState, PRPackedBool& aConstrainedSize);
inline PRBool HaveFixedSize(const nsHTMLReflowState& aReflowState)
{
NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition");
return HaveFixedSize(*(aReflowState.mStylePosition));
}
nsresult
NS_NewImageFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
@@ -236,6 +265,21 @@ nsImageFrame::Destroy(nsIPresContext* aPresContext)
mListener = nsnull;
// check / cleanup the IconLoads singleton
if (mIconLoad) {
#ifdef NOISY_ICON_LOADING
printf( "Releasing IconLoad (%p)\n", this);
#endif
if (mIconLoad->Release()) {
#ifdef NOISY_ICON_LOADING
printf( "Deleting IconLoad (%p)\n", this);
#endif
delete mIconLoad;
mIconLoad = nsnull;
}
}
return nsSplittableFrame::Destroy(aPresContext);
}
@@ -269,6 +313,8 @@ nsImageFrame::Init(nsIPresContext* aPresContext,
mInitialLoadCompleted = PR_FALSE;
mCanSendLoadEvent = PR_TRUE;
LoadIcons(aPresContext);
mLoads[0].mRequest = do_CreateInstance("@mozilla.org/image/request;1");
rv = LoadImage(src, aPresContext, mLoads[0].mRequest); // if the image was found in the cache,
// it is possible that LoadImage will result in
@@ -289,6 +335,11 @@ NS_IMETHODIMP nsImageFrame::OnStartContainer(imgIRequest *aRequest, nsIPresConte
mInitialLoadCompleted = PR_TRUE;
// handle iconLoads first...
if (HandleIconLoads(aRequest, PR_FALSE)) {
return NS_OK;
}
int whichLoad = GetImageLoad(aRequest);
if (whichLoad == -1) return NS_ERROR_FAILURE;
struct ImageLoad *load= &mLoads[whichLoad];
@@ -350,6 +401,14 @@ NS_IMETHODIMP nsImageFrame::OnDataAvailable(imgIRequest *aRequest, nsIPresContex
if (!aRect)
return NS_ERROR_NULL_POINTER;
// handle iconLoads first...
if (HandleIconLoads(aRequest, PR_FALSE)) {
if (!aRect->IsEmpty()) {
Invalidate(aPresContext, *aRect, PR_FALSE);
}
return NS_OK;
}
int whichLoad = GetImageLoad(aRequest);
if (whichLoad == -1) return NS_ERROR_FAILURE;
if (whichLoad != 0) return NS_OK;
@@ -533,6 +592,11 @@ NS_IMETHODIMP nsImageFrame::OnStopDecode(imgIRequest *aRequest, nsIPresContext *
// check to see if an image error occurred
PRBool imageFailedToLoad = PR_FALSE;
// handle iconLoads first...
if (HandleIconLoads(aRequest, NS_SUCCEEDED(aStatus))) {
return NS_OK;
}
int whichLoad = GetImageLoad(aRequest);
if (whichLoad == -1) return NS_ERROR_FAILURE;
@@ -564,7 +628,9 @@ NS_IMETHODIMP nsImageFrame::OnStopDecode(imgIRequest *aRequest, nsIPresContext *
nsSize s;
GetSize(s);
nsRect r(0,0, s.width, s.height);
Invalidate(aPresContext, r, PR_FALSE);
if (!r.IsEmpty()) {
Invalidate(aPresContext, r, PR_FALSE);
}
}
} else {
@@ -579,14 +645,42 @@ NS_IMETHODIMP nsImageFrame::OnStopDecode(imgIRequest *aRequest, nsIPresContext *
}
// if src failed then notify the PresShell
// if src failed to load, determine how to handle it:
// - either render the ALT text in this frame, or let the presShell handle it
if (imageFailedToLoad && presShell) {
if (mFailureReplace) {
// first check for image map
nsAutoString usemap;
mContent->GetAttr(kNameSpaceID_HTML, nsHTMLAtoms::usemap, usemap);
// We failed to load the image. Notify the pres shell if we aren't an image map
if (usemap.IsEmpty()) {
presShell->CantRenderReplacedElement(aPresContext, this);
// check if we want to honor the ALT text in the IMG frame, or let the preShell make it into inline text
// - if QuirksMode, and the IMG has a size, then render the ALT text in the ING frame
// UNLESS there is a pref set to force inline alt text
PRBool useSizedBox = PR_FALSE;
PRBool prefForceInlineAltText = mIconLoad ? mIconLoad->mPrefForceInlineAltText : PR_FALSE;
// check if we have fixed size
const nsStylePosition* stylePosition =
(const nsStylePosition*)mStyleContext->GetStyleData(eStyleStruct_Position);
NS_ASSERTION(stylePosition, "null style position: frame is corrupted");
// check for quirks mode
nsCompatibility mode;
aPresContext->GetCompatibilityMode(&mode);
// wrap it all up
useSizedBox = !prefForceInlineAltText &&
HaveFixedSize(*stylePosition) &&
mode == eCompatibility_NavQuirks;
if (!useSizedBox) {
// let the presShell handle converting this into the inline alt text frame
presShell->CantRenderReplacedElement(aPresContext, this);
} else {
// we are handling it
// invalidate the icon area (it may change states)
InvalidateIcon(aPresContext);
}
}
}
mFailureReplace = PR_TRUE;
@@ -627,8 +721,9 @@ NS_IMETHODIMP nsImageFrame::FrameChanged(imgIContainer *aContainer, nsIPresConte
r.height = NSIntPixelsToTwips(r.height, p2t);
mLoads[0].mTransform.TransformCoord(&r.x, &r.y, &r.width, &r.height);
Invalidate(aPresContext, r, PR_FALSE);
if (!r.IsEmpty()) {
Invalidate(aPresContext, r, PR_FALSE);
}
}
return NS_OK;
}
@@ -816,7 +911,7 @@ nsImageFrame::Reflow(nsIPresContext* aPresContext,
aStatus = NS_FRAME_COMPLETE;
// see if we have a frozen size (i.e. a fixed width and height)
HaveFixedSize(aReflowState, mSizeConstrained);
mSizeConstrained = HaveFixedSize(aReflowState);
if (aReflowState.reason == eReflowReason_Initial)
mGotInitialReflow = PR_TRUE;
@@ -1005,6 +1100,10 @@ struct nsRecessedBorder : public nsStyleBorder {
}
};
#define ICON_SIZE (16)
#define ICON_PADDING (6)
#define ALT_BORDER_WIDTH (1)
void
nsImageFrame::DisplayAltFeedback(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@@ -1018,7 +1117,7 @@ nsImageFrame::DisplayAltFeedback(nsIPresContext* aPresContext,
float p2t;
nscoord borderEdgeWidth;
aPresContext->GetScaledPixelsToTwips(&p2t);
borderEdgeWidth = NSIntPixelsToTwips(1, p2t);
borderEdgeWidth = NSIntPixelsToTwips(ALT_BORDER_WIDTH, p2t);
// Make sure we have enough room to actually render the border within
// our frame bounds
@@ -1033,7 +1132,8 @@ nsImageFrame::DisplayAltFeedback(nsIPresContext* aPresContext,
// Adjust the inner rect to account for the one pixel recessed border,
// and a six pixel padding on each edge
inner.Deflate(NSIntPixelsToTwips(7, p2t), NSIntPixelsToTwips(7, p2t));
inner.Deflate(NSIntPixelsToTwips(ICON_PADDING+ALT_BORDER_WIDTH, p2t),
NSIntPixelsToTwips(ICON_PADDING+ALT_BORDER_WIDTH, p2t));
if (inner.IsEmpty()) {
return;
}
@@ -1043,34 +1143,61 @@ nsImageFrame::DisplayAltFeedback(nsIPresContext* aPresContext,
aRenderingContext.PushState();
aRenderingContext.SetClipRect(inner, nsClipCombine_kIntersect, clipState);
// Display the icon
#ifdef USE_IMG2
// XXX
#else
nsIDeviceContext* dc;
aRenderingContext.GetDeviceContext(dc);
nsIImage* icon;
PRInt32 size = NSIntPixelsToTwips(ICON_SIZE, p2t);
if (NS_SUCCEEDED(dc->LoadIconImage(aIconId, icon)) && icon) {
aRenderingContext.DrawImage(icon, inner.x, inner.y);
PRBool iconUsed = PR_FALSE;
// Reduce the inner rect by the width of the icon, and leave an
// additional six pixels padding
PRInt32 iconWidth = NSIntPixelsToTwips(icon->GetWidth() + 6, p2t);
inner.x += iconWidth;
inner.width -= iconWidth;
// see if the icon images are present...
if (mIconLoad && mIconLoad->mIconsLoaded) {
// pick the correct image
NS_ASSERTION( aIconId == NS_ICON_LOADING_IMAGE ||
aIconId == NS_ICON_BROKEN_IMAGE, "Invalid Icon ID in DisplayAltFeedback");
NS_RELEASE(icon);
nsCOMPtr<imgIContainer> imgCon;
if (mIconLoad->mIconLoads[aIconId].mRequest) {
mIconLoad->mIconLoads[aIconId].mRequest->GetImage(getter_AddRefs(imgCon));
}
if (imgCon) {
// draw it
nsPoint p(inner.x, inner.y);
nsRect r(0,0,size,size);
r.x -= mBorderPadding.left;
r.y -= mBorderPadding.top;
aRenderingContext.DrawImage(imgCon, &r, &p);
iconUsed = PR_TRUE;
}
}
NS_RELEASE(dc);
#endif
// if we could not draw the image, then just draw some grafitti
if (!iconUsed) {
nscolor oldColor;
aRenderingContext.DrawRect(0,0,size,size);
aRenderingContext.GetColor(oldColor);
aRenderingContext.SetColor( aIconId == NS_ICON_BROKEN_IMAGE ? NS_RGB(0xFF,0,0) : NS_RGB(0,0xFF,0));
aRenderingContext.FillEllipse(NS_STATIC_CAST(int,size/2),NS_STATIC_CAST(int,size/2),
NS_STATIC_CAST(int,(size/2)-(2*p2t)),NS_STATIC_CAST(int,(size/2)-(2*p2t)));
aRenderingContext.SetColor(oldColor);
}
// Reduce the inner rect by the width of the icon, and leave an
// additional ICON_PADDING pixels for padding
PRInt32 iconWidth = NSIntPixelsToTwips(ICON_SIZE + ICON_PADDING, p2t);
inner.x += iconWidth;
inner.width -= iconWidth;
// If there's still room, display the alt-text
if (!inner.IsEmpty()) {
nsAutoString altText;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_HTML, nsHTMLAtoms::alt, altText)) {
DisplayAltText(aPresContext, aRenderingContext, altText, inner);
nsCOMPtr<nsIContent> content;
nsCOMPtr<nsIAtom> tag;
nsAutoString altText;
GetContent(getter_AddRefs(content));
if (content) {
content->GetTag(*getter_AddRefs(tag));
if (tag) {
nsCSSFrameConstructor::GetAlternateTextFor(content, tag, altText);
DisplayAltText(aPresContext, aRenderingContext, altText, inner);
}
}
}
@@ -1132,14 +1259,16 @@ nsImageFrame::Paint(nsIPresContext* aPresContext,
if (loadStatus & imgIRequest::STATUS_ERROR || !imgCon) {
// No image yet, or image load failed. Draw the alt-text and an icon
// indicating the status
#ifndef SUPPRESS_LOADING_ICON
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
#else
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer &&
!mInitialLoadCompleted) {
DisplayAltFeedback(aPresContext, aRenderingContext, 0);
#ifndef USE_IMG2
mInitialLoadCompleted) {
#endif
DisplayAltFeedback(aPresContext, aRenderingContext,
(loadStatus & imgIRequest::STATUS_ERROR)
? NS_ICON_BROKEN_IMAGE
: NS_ICON_LOADING_IMAGE);
#endif
}
}
else {
@@ -1711,21 +1840,6 @@ nsImageFrame::IsImageComplete(PRBool* aComplete)
return NS_OK;
}
void HaveFixedSize(const nsHTMLReflowState& aReflowState, PRPackedBool& aConstrainedSize)
{
// check the width and height values in the reflow state's style struct
// - if width and height are specified as either coord or percentage, then
// the size of the image frame is constrained
nsStyleUnit widthUnit = aReflowState.mStylePosition->mWidth.GetUnit();
nsStyleUnit heightUnit = aReflowState.mStylePosition->mHeight.GetUnit();
aConstrainedSize =
((widthUnit == eStyleUnit_Coord ||
widthUnit == eStyleUnit_Percent) &&
(heightUnit == eStyleUnit_Coord ||
heightUnit == eStyleUnit_Percent));
}
nsresult
nsImageFrame::LoadImage(const nsAReadableString& aSpec, nsIPresContext *aPresContext, imgIRequest *aRequest)
{
@@ -1920,7 +2034,119 @@ nsImageFrame::CanLoadImage(nsIURI *aURI)
return shouldLoad;
}
nsresult nsImageFrame::LoadIcons(nsIPresContext *aPresContext)
{
// XXX
// XXX: change these icons!
// XXX
nsAutoString loadingSrc(NS_LITERAL_STRING("chrome://editor/content/images/undecided_n.gif"));
nsAutoString brokenSrc(NS_LITERAL_STRING("chrome://editor/content/images/tongue_n.gif"));
nsresult rv = NS_OK;
PRBool doLoad = PR_FALSE; // only load icons once...
// see if the first instance and we need to create the icon loader
if (!mIconLoad) {
#ifdef NOISY_ICON_LOADING
printf( "Allocating IconLoad (%p)\n", this);
#endif
mIconLoad = new IconLoad(aPresContext);
if (!mIconLoad)
return NS_ERROR_OUT_OF_MEMORY;
doLoad = PR_TRUE;
}
// always addref
mIconLoad->AddRef();
#ifdef NOISY_ICON_LOADING
printf( "IconLoad AddRef'd(%p)\n", this);
#endif
// if already loaded, we are done
if (mIconLoad->mIconsLoaded) return rv;
if (doLoad) {
// create a loader and load the images
mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest = do_CreateInstance("@mozilla.org/image/request;1");
if (mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest) {
#ifdef NOISY_ICON_LOADING
printf( "Loading request %p\n", mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest.get() );
#endif
rv = LoadImage(loadingSrc, aPresContext, mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest);
if (NS_SUCCEEDED(rv)) {
mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest = do_CreateInstance("@mozilla.org/image/request;1");
if (mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest) {
#ifdef NOISY_ICON_LOADING
printf( "Loading request %p\n", mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest.get() );
#endif
rv = LoadImage(brokenSrc, aPresContext, mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest);
// ImageLoader will callback into OnStartContainer, which will handle the mIconsLoaded flag
}
}
}
}
return rv;
}
PRBool nsImageFrame::HandleIconLoads(imgIRequest* aRequest, PRBool aLoaded)
{
PRBool result = PR_FALSE;
if( mIconLoad ) {
// check which image it is
if (aRequest == mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest.get()) {
result = PR_TRUE;
} else if (aRequest == mIconLoad->mIconLoads[NS_ICON_BROKEN_IMAGE].mRequest.get()) {
result = PR_TRUE;
}
if (result) {
mIconLoad->mIconsLoaded = aLoaded;
}
#ifdef NOISY_ICON_LOADING
if (mIconLoad->mIconsLoaded && result) {
printf( "Icons Loaded: request for %s\n", aRequest == mIconLoad->mIconLoads[NS_ICON_LOADING_IMAGE].mRequest ?
"NS_ICON_LOADING_IMAGE" : "NS_ICON_BROKEN_IMAGE" );
}
#endif
}
#ifdef NOISY_ICON_LOADING
printf( "HandleIconLoads returned %s (%p)\n", result ? "TRUE" : "FALSE", this);
#endif
return result;
}
void nsImageFrame::InvalidateIcon(nsIPresContext *aPresContext)
{
NS_ASSERTION(aPresContext, "null presContext in InvalidateIcon");
float p2t;
aPresContext->GetScaledPixelsToTwips(&p2t);
nsRect inner;
GetInnerArea(aPresContext, inner);
nsRect rect(inner.x,
inner.y,
NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING, p2t),
NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING, p2t));
Invalidate(aPresContext, rect, PR_FALSE);
}
void nsImageFrame::IconLoad::GetAltModePref(nsIPresContext *aPresContext)
{
NS_ASSERTION(aPresContext, "null presContext is not allowed in GetAltModePref");
// NOTE: the presContext could be used to fetch a cached pref if needed, but is not for now
nsCOMPtr<nsIPref> prefs = do_GetService(NS_PREF_CONTRACTID);
if (prefs) {
PRBool boolPref;
if (NS_SUCCEEDED(prefs->GetBoolPref("browser.display.force_inline_alttext", &boolPref))) {
mPrefForceInlineAltText = boolPref;
} else {
mPrefForceInlineAltText = PR_FALSE;
}
}
}
#ifdef DEBUG
NS_IMETHODIMP

View File

@@ -250,6 +250,31 @@ private:
nsMargin mBorderPadding;
PRUint32 mNaturalImageWidth,
mNaturalImageHeight;
/* loading / broken image icon support */
// LoadIcons: initiate the loading of the static icons used to show loading / broken images
nsresult LoadIcons(nsIPresContext *aPresContext);
// HandleIconLoads: See if the request is for an Icon load. If it is, handle it and return TRUE
// otherwise, return FALSE (aCompleted is an input arg telling the routine if the request has completed)
PRBool HandleIconLoads(imgIRequest* aRequest, PRBool aCompleted);
void InvalidateIcon(nsIPresContext *aPresContext);
class IconLoad {
// private class that wraps the data and logic needed for
// broken image and loading image icons
public:
IconLoad(nsIPresContext *aPresContext):mRefCount(0),mIconsLoaded(PR_FALSE) { GetAltModePref(aPresContext); }
void AddRef(void) { ++mRefCount; }
PRBool Release(void) { return --mRefCount == 0; }
void GetAltModePref(nsIPresContext *aPresContext);
PRUint32 mRefCount;
struct ImageLoad mIconLoads[2]; // 0 is for the 'loading' icon, 1 is for the 'broken' icon
PRPackedBool mIconsLoaded;
PRPackedBool mPrefForceInlineAltText;
};
static IconLoad* mIconLoad; // singleton patern: one LoadIcons instance is used
};
#endif /* nsImageFrame_h___ */