/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape 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/NPL/ * * 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) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * 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 NPL, 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 NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsHTMLParts.h" #include "nsCOMPtr.h" #include "nsImageFrame.h" #include "nsString.h" #include "nsIPresContext.h" #include "nsIRenderingContext.h" #include "nsIPresShell.h" #include "nsIImage.h" #include "nsIWidget.h" #include "nsHTMLAtoms.h" #include "nsIHTMLContent.h" #include "nsIDocument.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" #include "nsImageMap.h" #include "nsILinkHandler.h" #include "nsIURL.h" #include "nsIIOService.h" #include "nsIURL.h" #include "nsILoadGroup.h" #include "nsIServiceManager.h" #include "nsNetUtil.h" #include "nsIView.h" #include "nsIViewManager.h" #include "nsHTMLContainerFrame.h" #include "prprf.h" #include "nsIFontMetrics.h" #include "nsCSSRendering.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIDOMHTMLImageElement.h" #include "nsIDeviceContext.h" #include "nsINameSpaceManager.h" #include "nsTextFragment.h" #include "nsIDOMHTMLMapElement.h" #include "nsIStyleSet.h" #include "nsLayoutAtoms.h" #include "nsISizeOfHandler.h" #include "nsImageMapUtils.h" #include "nsIFrameManager.h" #include "nsIScriptSecurityManager.h" #ifdef ACCESSIBILITY #include "nsIAccessibilityService.h" #endif #include "nsIServiceManager.h" #include "nsIDOMNode.h" #include "nsGUIEvent.h" #include "imgIContainer.h" #include "imgILoader.h" #include "nsIEventQueueService.h" #include "plevent.h" #include "nsContentPolicyUtils.h" #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 // Value's for mSuppress #define SUPPRESS_UNSET 0 #define DONT_SUPPRESS 1 #define SUPPRESS 2 #define DEFAULT_SUPPRESS 3 // values for image loading icons #define NS_ICON_LOADING_IMAGE (0) #define NS_ICON_BROKEN_IMAGE (1) // sizes (pixels) for image icon, padding and border frame #define ICON_SIZE (16) #define ICON_PADDING (3) #define ALT_BORDER_WIDTH (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 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) { NS_PRECONDITION(aNewFrame, "null OUT ptr"); if (nsnull == aNewFrame) { return NS_ERROR_NULL_POINTER; } nsImageFrame* it = new (aPresShell) nsImageFrame; if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } *aNewFrame = it; return NS_OK; } int nsImageFrame::GetImageLoad(imgIRequest *aRequest) { if (aRequest == mLoads[0].mRequest) return 0; else if (aRequest == mLoads[1].mRequest) return 1; NS_ASSERTION((aRequest == mLoads[0].mRequest && aRequest == mLoads[1].mRequest), "Failure to figure out which imgIRequest this is!"); return -1; } nsImageFrame::nsImageFrame() : mIntrinsicSize(0, 0), mGotInitialReflow(PR_FALSE), mImageBlocked(PR_FALSE), mFailureReplace(PR_TRUE) { // Size is constrained if we have a width and height. // - Set in reflow in case the attributes are changed mSizeConstrained = PR_FALSE; } nsImageFrame::~nsImageFrame() { } NS_IMETHODIMP nsImageFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { NS_ENSURE_ARG_POINTER(aInstancePtr); *aInstancePtr = nsnull; #ifdef DEBUG if (aIID.Equals(NS_GET_IID(nsIFrameDebug))) { *aInstancePtr = NS_STATIC_CAST(nsIFrameDebug*,this); return NS_OK; } #endif if (aIID.Equals(NS_GET_IID(nsIImageFrame))) { *aInstancePtr = NS_STATIC_CAST(nsIImageFrame*,this); return NS_OK; } else if (aIID.Equals(NS_GET_IID(nsIFrame))) { *aInstancePtr = NS_STATIC_CAST(nsIFrame*,this); return NS_OK; } else if (aIID.Equals(NS_GET_IID(nsISupports))) { *aInstancePtr = NS_STATIC_CAST(nsIImageFrame*,this); return NS_OK; } return NS_NOINTERFACE; } #ifdef ACCESSIBILITY NS_IMETHODIMP nsImageFrame::GetAccessible(nsIAccessible** aAccessible) { nsCOMPtr accService = do_GetService("@mozilla.org/accessibilityService;1"); if (accService) { return accService->CreateHTMLImageAccessible(NS_STATIC_CAST(nsIFrame*, this), aAccessible); } return NS_ERROR_FAILURE; } #endif NS_IMETHODIMP_(nsrefcnt) nsImageFrame::AddRef(void) { NS_WARNING("not supported for frames"); return 1; } NS_IMETHODIMP_(nsrefcnt) nsImageFrame::Release(void) { NS_WARNING("not supported for frames"); return 1; } NS_IMETHODIMP nsImageFrame::Destroy(nsIPresContext* aPresContext) { // Tell our image map, if there is one, to clean up // This causes the nsImageMap to unregister itself as // a DOM listener. if(mImageMap) { mImageMap->Destroy(); NS_RELEASE(mImageMap); } for (int i=0; i != 2; ++i) { if (mLoads[i].mRequest) { mLoads[i].mRequest->Cancel(NS_ERROR_FAILURE); mLoads[i].mRequest = nsnull; } } // set the frame to null so we don't send messages to a dead object. if (mListener) NS_REINTERPRET_CAST(nsImageListener*, mListener.get())->SetFrame(nsnull); 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); } NS_IMETHODIMP nsImageFrame::Init(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, nsIStyleContext* aContext, nsIFrame* aPrevInFlow) { nsresult rv = nsSplittableFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow); // See if we have a SRC attribute nsAutoString src; nsresult ca; ca = mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::src, src); if ((NS_CONTENT_ATTR_HAS_VALUE != ca) || (src.Length() == 0)) { // Let's see if this is an object tag and we have a DATA attribute nsIAtom* tag; mContent->GetTag(tag); if(tag == nsHTMLAtoms::object) mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::data, src); NS_IF_RELEASE(tag); } 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 // a call to OnStartContainer() return rv; } NS_IMETHODIMP nsImageFrame::OnStartDecode(imgIRequest *aRequest, nsIPresContext *aPresContext) { return NS_OK; } NS_IMETHODIMP nsImageFrame::OnStartContainer(imgIRequest *aRequest, nsIPresContext *aPresContext, imgIContainer *aImage) { if (!aImage) return NS_ERROR_INVALID_ARG; 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]; if (aImage) { /* Get requested animation policy from the pres context: * normal = 0 * one frame = 1 * one loop = 2 */ PRUint16 animateMode = imgIContainer::kNormalAnimMode; //default value nsresult rv = aPresContext->GetImageAnimationMode(&animateMode); if (NS_SUCCEEDED(rv)) aImage->SetAnimationMode(animateMode); } nscoord w, h; aImage->GetWidth(&w); aImage->GetHeight(&h); float p2t; aPresContext->GetPixelsToTwips(&p2t); nsSize newsize(NSIntPixelsToTwips(w, p2t), NSIntPixelsToTwips(h, p2t)); if (load->mIntrinsicSize != newsize) { load->mIntrinsicSize = newsize; if (load->mIntrinsicSize.width != 0 && load->mIntrinsicSize.height != 0) load->mTransform.SetToScale((float(mComputedSize.width) / float(load->mIntrinsicSize.width)), (float(mComputedSize.height) / float(load->mIntrinsicSize.height))); if (!mSizeConstrained) { nsCOMPtr presShell; aPresContext->GetShell(getter_AddRefs(presShell)); NS_ASSERTION(mParent, "No parent to pass the reflow request up to."); NS_ASSERTION(presShell, "No PresShell."); if (mParent && presShell && mGotInitialReflow && whichLoad == 0) { // don't reflow if we havn't gotten the inital reflow yet mState |= NS_FRAME_IS_DIRTY; mParent->ReflowDirtyChild(presShell, NS_STATIC_CAST(nsIFrame*, this)); } } } return NS_OK; } NS_IMETHODIMP nsImageFrame::OnStartFrame(imgIRequest *aRequest, nsIPresContext *aPresContext, gfxIImageFrame *aFrame) { return NS_OK; } NS_IMETHODIMP nsImageFrame::OnDataAvailable(imgIRequest *aRequest, nsIPresContext *aPresContext, gfxIImageFrame *aFrame, const nsRect *aRect) { // XXX do we need to make sure that the reflow from the OnStartContainer has been // processed before we start calling invalidate 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; struct ImageLoad *load= &mLoads[whichLoad]; nsRect r(aRect->x, aRect->y, aRect->width, aRect->height); float p2t; aPresContext->GetPixelsToTwips(&p2t); r.x = NSIntPixelsToTwips(r.x, p2t); r.y = NSIntPixelsToTwips(r.y, p2t); r.width = NSIntPixelsToTwips(r.width, p2t); r.height = NSIntPixelsToTwips(r.height, p2t); load->mTransform.TransformCoord(&r.x, &r.y, &r.width, &r.height); r.x += mBorderPadding.left; r.y += mBorderPadding.top; if (whichLoad == 0 && !r.IsEmpty()) Invalidate(aPresContext, r, PR_FALSE); return NS_OK; } NS_IMETHODIMP nsImageFrame::OnStopFrame(imgIRequest *aRequest, nsIPresContext *aPresContext, gfxIImageFrame *aFrame) { return NS_OK; } NS_IMETHODIMP nsImageFrame::OnStopContainer(imgIRequest *aRequest, nsIPresContext *aPresContext, imgIContainer *aImage) { return NS_OK; } static void PR_CALLBACK HandleImagePLEvent(nsIContent *aContent, PRUint32 aMessage, PRUint32 aFlags) { nsCOMPtr node(do_QueryInterface(aContent)); if (!node) { NS_ERROR("null or non-DOM node passed to HandleImagePLEvent!"); return; } nsCOMPtr dom_doc; node->GetOwnerDocument(getter_AddRefs(dom_doc)); nsCOMPtr doc(do_QueryInterface(dom_doc)); if (!doc) { return; } nsCOMPtr pres_shell; doc->GetShellAt(0, getter_AddRefs(pres_shell)); if (!pres_shell) { return; } nsCOMPtr pres_context; pres_shell->GetPresContext(getter_AddRefs(pres_context)); if (!pres_context) { return; } nsEventStatus status = nsEventStatus_eIgnore; nsEvent event; event.eventStructType = NS_EVENT; event.message = aMessage; aContent->HandleDOMEvent(pres_context, &event, nsnull, aFlags, &status); } static void PR_CALLBACK HandleImageOnloadPLEvent(PLEvent *aEvent) { nsIContent *content = (nsIContent *)PL_GetEventOwner(aEvent); HandleImagePLEvent(content, NS_IMAGE_LOAD, NS_EVENT_FLAG_INIT | NS_EVENT_FLAG_CANT_BUBBLE); NS_RELEASE(content); } static void PR_CALLBACK HandleImageOnerrorPLEvent(PLEvent *aEvent) { nsIContent *content = (nsIContent *)PL_GetEventOwner(aEvent); HandleImagePLEvent(content, NS_IMAGE_ERROR, NS_EVENT_FLAG_INIT); NS_RELEASE(content); } static void PR_CALLBACK DestroyImagePLEvent(PLEvent* aEvent) { delete aEvent; } // Fire off a PLEvent that'll asynchronously call the image elements // onload handler once handled. This is needed since the image library // can't decide if it wants to call it's observer methods // synchronously or asynchronously. If an image is loaded from the // cache the notifications come back synchronously, but if the image // is loaded from the netswork the notifications come back // asynchronously. void nsImageFrame::FireDOMEvent(PRUint32 aMessage) { static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); nsCOMPtr event_service = do_GetService(kEventQueueServiceCID); if (!event_service) { NS_WARNING("Failed to get event queue service"); return; } nsCOMPtr event_queue; event_service->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(event_queue)); if (!event_queue) { NS_WARNING("Failed to get event queue from service"); return; } PLEvent *event = new PLEvent; if (!event) { // Out of memory, but none of our callers care, so just warn and // don't fire the event NS_WARNING("Out of memory?"); return; } PLHandleEventProc f; switch (aMessage) { case NS_IMAGE_LOAD : f = (PLHandleEventProc)::HandleImageOnloadPLEvent; break; case NS_IMAGE_ERROR : f = (PLHandleEventProc)::HandleImageOnerrorPLEvent; break; default: NS_WARNING("Huh, I don't know how to fire this type of event?!"); return; } PL_InitEvent(event, mContent, f, (PLDestroyEventProc)::DestroyImagePLEvent); // The event owns the content pointer now. NS_ADDREF(mContent); event_queue->PostEvent(event); } NS_IMETHODIMP nsImageFrame::OnStopDecode(imgIRequest *aRequest, nsIPresContext *aPresContext, nsresult aStatus, const PRUnichar *aStatusArg) { nsCOMPtr presShell; aPresContext->GetShell(getter_AddRefs(presShell)); // 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; if (whichLoad == 1) { if (NS_SUCCEEDED(aStatus)) { if (mLoads[0].mRequest) { mLoads[0].mRequest->Cancel(NS_ERROR_FAILURE); } mLoads[0].mRequest = mLoads[1].mRequest; mLoads[0].mIntrinsicSize = mLoads[1].mIntrinsicSize; // XXX i don't think we always want to set this. mLoads[0].mTransform = mLoads[1].mTransform; mLoads[1].mRequest = nsnull; } else { // load failed, we need to reset mLoads[0] to get an broken icon // since the src attribute changed mLoads[0].mRequest = nsnull; mLoads[1].mRequest = nsnull; imageFailedToLoad = PR_TRUE; } if (!mSizeConstrained && (mLoads[0].mIntrinsicSize != mIntrinsicSize)) { nsCOMPtr presShell; aPresContext->GetShell(getter_AddRefs(presShell)); NS_ASSERTION(mParent, "No parent to pass the reflow request up to."); NS_ASSERTION(presShell, "No PresShell."); if (mParent && presShell && mGotInitialReflow) { // don't reflow if we havn't gotten the inital reflow yet mState |= NS_FRAME_IS_DIRTY; mParent->ReflowDirtyChild(presShell, NS_STATIC_CAST(nsIFrame*, this)); } } else { nsSize s; GetSize(s); nsRect r(0,0, s.width, s.height); if (!r.IsEmpty()) { Invalidate(aPresContext, r, PR_FALSE); } } } else if (NS_FAILED(aStatus)) { if (whichLoad == 0 || !mLoads[0].mRequest) { imageFailedToLoad = PR_TRUE; } } // 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_None, nsHTMLAtoms::usemap, usemap); if (usemap.IsEmpty()) { // 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 IMG 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); // check for being in Composer: if we are, we always show the size-box PRBool forceIcon = PR_FALSE; // check for style property that indicates the icon should always be shown const nsStyleUIReset* styleData; GetStyleData(eStyleStruct_UIReset, (const nsStyleStruct*&) styleData); if (styleData->mForceBrokenImageIcon) { forceIcon = PR_TRUE; } // wrap it all up: use the size box if // - in editor // - not forcing inline alt text, have fixed size, in quirks mode, image not blocked useSizedBox = forceIcon || (!prefForceInlineAltText && HaveFixedSize(*stylePosition) && mode == eCompatibility_NavQuirks && !mImageBlocked); 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; } // After these DOM events are fired its possible that this frame may be deleted. As a result // the code should not attempt to access any of the frames internal data after this point. if (presShell) { if (imageFailedToLoad) { // Send error event FireDOMEvent(NS_IMAGE_ERROR); } else if (mCanSendLoadEvent) { // Send load event mCanSendLoadEvent = PR_FALSE; FireDOMEvent(NS_IMAGE_LOAD); } } return NS_OK; } NS_IMETHODIMP nsImageFrame::FrameChanged(imgIContainer *aContainer, nsIPresContext *aPresContext, gfxIImageFrame *aNewFrame, nsRect *aDirtyRect) { if (!mLoads[0].mRequest) return NS_OK; // if mLoads[0].mRequest is null, this isn't for the first one, so we don't care about it. const nsStyleVisibility* vis; ::GetStyleData(this, &vis); if (!vis->IsVisible()) return NS_OK; nsCOMPtr con; mLoads[0].mRequest->GetImage(getter_AddRefs(con)); if (aContainer == con.get()) { nsRect r(*aDirtyRect); float p2t; aPresContext->GetPixelsToTwips(&p2t); r.x = NSIntPixelsToTwips(r.x, p2t); r.y = NSIntPixelsToTwips(r.y, p2t); r.width = NSIntPixelsToTwips(r.width, p2t); r.height = NSIntPixelsToTwips(r.height, p2t); mLoads[0].mTransform.TransformCoord(&r.x, &r.y, &r.width, &r.height); if (!r.IsEmpty()) { Invalidate(aPresContext, r, PR_FALSE); } } return NS_OK; } #define MINMAX(_value,_min,_max) \ ((_value) < (_min) \ ? (_min) \ : ((_value) > (_max) \ ? (_max) \ : (_value))) void nsImageFrame::GetDesiredSize(nsIPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize) { // if mLoads[0].mIntrinsicSize.width and height are 0, then we should // check to see if the size is already known by the image container. if (mLoads[0].mIntrinsicSize.width == 0 && mLoads[0].mIntrinsicSize.height == 0) { PRBool needMinIconSizing = PR_FALSE; float p2t; aPresContext->GetPixelsToTwips(&p2t); if (mLoads[0].mRequest) { nsCOMPtr con; mLoads[0].mRequest->GetImage(getter_AddRefs(con)); if (con) { nscoord width, height; con->GetWidth(&width); con->GetHeight(&height); mLoads[0].mIntrinsicSize.width = NSIntPixelsToTwips(width, p2t); mLoads[0].mIntrinsicSize.height = NSIntPixelsToTwips(height, p2t); } else { // no image container, so we may need to synthesize a minimum size for the image icons needMinIconSizing = PR_TRUE; } } else { // no request, so we may need to synthesize a minimum size for the image icons needMinIconSizing = PR_TRUE; } if (needMinIconSizing) { // XXX: we need this in composer, but it is also good for general quirks mode to always // XXX: have room for the icon // check for quirks mode nsCompatibility mode; aPresContext->GetCompatibilityMode(&mode); if (mode == eCompatibility_NavQuirks) { // image request is null or image size not known, probably an invalid image specified // - make the image big enough for the icon (it may not be used if inline alt expansion is used instead) mLoads[0].mIntrinsicSize.width = NSIntPixelsToTwips(ICON_SIZE+(2*(ICON_PADDING+ALT_BORDER_WIDTH)), p2t); mLoads[0].mIntrinsicSize.height = NSIntPixelsToTwips(ICON_SIZE+(2*(ICON_PADDING+ALT_BORDER_WIDTH)), p2t); } } } mIntrinsicSize = mLoads[0].mIntrinsicSize; float t2p, sp2t; aPresContext->GetTwipsToPixels(&t2p); aPresContext->GetScaledPixelsToTwips(&sp2t); // convert from normal twips to scaled twips (printing...) float t2st = t2p * sp2t; // twips to scaled twips nscoord intrinsicWidth = NSToCoordRound(float(mIntrinsicSize.width) * t2st); nscoord intrinsicHeight = NSToCoordRound(float(mIntrinsicSize.height) * t2st); // Determine whether the image has fixed content width nscoord widthConstraint = aReflowState.mComputedWidth; nscoord minWidth = aReflowState.mComputedMinWidth; nscoord maxWidth = aReflowState.mComputedMaxWidth; // Determine whether the image has fixed content height nscoord heightConstraint = aReflowState.mComputedHeight; nscoord minHeight = aReflowState.mComputedMinHeight; nscoord maxHeight = aReflowState.mComputedMaxHeight; PRBool isAutoWidth = widthConstraint == NS_INTRINSICSIZE; if (isAutoWidth) widthConstraint = intrinsicWidth; PRBool isAutoHeight = heightConstraint == NS_UNCONSTRAINEDSIZE; if (isAutoHeight) heightConstraint = intrinsicHeight; nscoord newWidth = MINMAX(widthConstraint, minWidth, maxWidth); nscoord newHeight = MINMAX(heightConstraint, minHeight, maxHeight); if (isAutoWidth && newWidth != intrinsicWidth) isAutoWidth = PR_FALSE; if (isAutoHeight && newHeight != intrinsicHeight) isAutoHeight = PR_FALSE; if (isAutoWidth) { if (!isAutoHeight && intrinsicHeight != 0) { newWidth = (intrinsicWidth * newHeight) / intrinsicHeight; } } else { if (isAutoHeight && intrinsicWidth != 0) { newHeight = (intrinsicHeight * newWidth) / intrinsicWidth; } } mComputedSize.width = newWidth; mComputedSize.height = newHeight; nsTransform2D *transform = &mLoads[0].mTransform; if (mComputedSize == mIntrinsicSize) { transform->SetToIdentity(); } else { if (mIntrinsicSize.width != 0 && mIntrinsicSize.height != 0) { transform->SetToScale(float(mComputedSize.width) / float(mIntrinsicSize.width), float(mComputedSize.height) / float(mIntrinsicSize.height)); } } aDesiredSize.width = mComputedSize.width; aDesiredSize.height = mComputedSize.height; } void nsImageFrame::GetInnerArea(nsIPresContext* aPresContext, nsRect& aInnerArea) const { aInnerArea.x = mBorderPadding.left; aInnerArea.y = mBorderPadding.top; aInnerArea.width = mRect.width - (mBorderPadding.left + mBorderPadding.right); aInnerArea.height = (!mPrevInFlow && !mNextInFlow) ? mRect.height - mBorderPadding.top - mBorderPadding.bottom : mComputedSize.height; } NS_IMETHODIMP nsImageFrame::ContentChanged(nsIPresContext* aPresContext, nsIContent* aChild, nsISupports* aSubContent) { nsCOMPtr shell; aPresContext->GetShell(getter_AddRefs(shell)); mState |= NS_FRAME_IS_DIRTY; return mParent->ReflowDirtyChild(shell, this); } // get the offset into the content area of the image where aImg starts if it is a continuation. nscoord nsImageFrame::GetContinuationOffset(nscoord* aWidth) const { nscoord offset = 0; if (aWidth) { *aWidth = 0; } if (mPrevInFlow) { for (nsIFrame* prevInFlow = mPrevInFlow ; prevInFlow; prevInFlow->GetPrevInFlow(&prevInFlow)) { nsRect rect; prevInFlow->GetRect(rect); if (aWidth) { *aWidth = rect.width; } offset += rect.height; } offset -= mBorderPadding.top; offset = PR_MAX(0, offset); } return offset; } NS_IMETHODIMP nsImageFrame::Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsImageFrame", aReflowState.reason); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsImageFrame::Reflow: availSize=%d,%d", aReflowState.availableWidth, aReflowState.availableHeight)); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; // see if we have a frozen size (i.e. a fixed width and height) mSizeConstrained = HaveFixedSize(aReflowState); if (aReflowState.reason == eReflowReason_Initial) mGotInitialReflow = PR_TRUE; // get the desired size of the complete image GetDesiredSize(aPresContext, aReflowState, aMetrics); // add borders and padding mBorderPadding = aReflowState.mComputedBorderPadding; aMetrics.width += mBorderPadding.left + mBorderPadding.right; aMetrics.height += mBorderPadding.top + mBorderPadding.bottom; if (mPrevInFlow) { nscoord y = GetContinuationOffset(&aMetrics.width); aMetrics.height -= y + mBorderPadding.top; aMetrics.height = PR_MAX(0, aMetrics.height); } // we have to split images if we are: // in Paginated mode, we need to have a constrained height, and have a height larger than our available height PRBool isPaginated; aPresContext->IsPaginated(&isPaginated); PRUint32 loadStatus = imgIRequest::STATUS_NONE; if (mLoads[0].mRequest) { mLoads[0].mRequest->GetImageStatus(&loadStatus); } if (isPaginated && ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || mSizeConstrained) && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) && (aMetrics.height > aReflowState.availableHeight)) { nsCOMPtr fType; GetFrameType(getter_AddRefs(fType)); // split an image frame but not an image control frame if (nsLayoutAtoms::imageFrame == fType.get()) { float p2t; aPresContext->GetScaledPixelsToTwips(&p2t); // our desired height was greater than 0, so to avoid infinite splitting, use 1 pixel as the min aMetrics.height = PR_MAX(NSToCoordRound(p2t), aReflowState.availableHeight); aStatus = NS_FRAME_NOT_COMPLETE; } } aMetrics.ascent = aMetrics.height; aMetrics.descent = 0; if (nsnull != aMetrics.maxElementSize) { // If we have a percentage based width, then our MES width is 0 if (eStyleUnit_Percent == aReflowState.mStylePosition->mWidth.GetUnit()) { aMetrics.maxElementSize->width = 0; } else { aMetrics.maxElementSize->width = aMetrics.width; } aMetrics.maxElementSize->height = aMetrics.height; } if (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH) { aMetrics.mMaximumWidth = aMetrics.width; } NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsImageFrame::Reflow: size=%d,%d", aMetrics.width, aMetrics.height)); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); return NS_OK; } // Computes the width of the specified string. aMaxWidth specifies the maximum // width available. Once this limit is reached no more characters are measured. // The number of characters that fit within the maximum width are returned in // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected // into the rendering context before this is called (for performance). MMP void nsImageFrame::MeasureString(const PRUnichar* aString, PRInt32 aLength, nscoord aMaxWidth, PRUint32& aMaxFit, nsIRenderingContext& aContext) { nscoord totalWidth = 0; nscoord spaceWidth; aContext.GetWidth(' ', spaceWidth); aMaxFit = 0; while (aLength > 0) { // Find the next place we can line break PRUint32 len = aLength; PRBool trailingSpace = PR_FALSE; for (PRInt32 i = 0; i < aLength; i++) { if (XP_IS_SPACE(aString[i]) && (i > 0)) { len = i; // don't include the space when measuring trailingSpace = PR_TRUE; break; } } // Measure this chunk of text, and see if it fits nscoord width; aContext.GetWidth(aString, len, width); PRBool fits = (totalWidth + width) <= aMaxWidth; // If it fits on the line, or it's the first word we've processed then // include it if (fits || (0 == totalWidth)) { // New piece fits totalWidth += width; // If there's a trailing space then see if it fits as well if (trailingSpace) { if ((totalWidth + spaceWidth) <= aMaxWidth) { totalWidth += spaceWidth; } else { // Space won't fit. Leave it at the end but don't include it in // the width fits = PR_FALSE; } len++; } aMaxFit += len; aString += len; aLength -= len; } if (!fits) { break; } } } // Formats the alt-text to fit within the specified rectangle. Breaks lines // between words if a word would extend past the edge of the rectangle void nsImageFrame::DisplayAltText(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsString& aAltText, const nsRect& aRect) { const nsStyleColor* color = (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color); // Set font and color aRenderingContext.SetColor(color->mColor); SetFontFromStyle(&aRenderingContext, mStyleContext); // Format the text to display within the formatting rect nsIFontMetrics* fm; aRenderingContext.GetFontMetrics(fm); nscoord maxAscent, maxDescent, height; fm->GetMaxAscent(maxAscent); fm->GetMaxDescent(maxDescent); fm->GetHeight(height); // XXX It would be nice if there was a way to have the font metrics tell // use where to break the text given a maximum width. At a minimum we need // to be able to get the break character... const PRUnichar* str = aAltText.get(); PRInt32 strLen = aAltText.Length(); nscoord y = aRect.y; while ((strLen > 0) && ((y + maxDescent) < aRect.YMost())) { // Determine how much of the text to display on this line PRUint32 maxFit; // number of characters that fit MeasureString(str, strLen, aRect.width, maxFit, aRenderingContext); // Display the text aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent); // Move to the next line str += maxFit; strLen -= maxFit; y += height; } NS_RELEASE(fm); } struct nsRecessedBorder : public nsStyleBorder { nsRecessedBorder(nscoord aBorderWidth) : nsStyleBorder() { nsStyleCoord styleCoord(aBorderWidth); mBorder.SetLeft(styleCoord); mBorder.SetTop(styleCoord); mBorder.SetRight(styleCoord); mBorder.SetBottom(styleCoord); mBorderStyle[0] = NS_STYLE_BORDER_STYLE_INSET; mBorderStyle[1] = NS_STYLE_BORDER_STYLE_INSET; mBorderStyle[2] = NS_STYLE_BORDER_STYLE_INSET; mBorderStyle[3] = NS_STYLE_BORDER_STYLE_INSET; mBorderColor[0] = 0; mBorderColor[1] = 0; mBorderColor[2] = 0; mBorderColor[3] = 0; mHasCachedBorder = PR_FALSE; } }; void nsImageFrame::DisplayAltFeedback(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, PRInt32 aIconId) { // Calculate the inner area nsRect inner; GetInnerArea(aPresContext, inner); // Display a recessed one pixel border float p2t; nscoord borderEdgeWidth; aPresContext->GetScaledPixelsToTwips(&p2t); borderEdgeWidth = NSIntPixelsToTwips(ALT_BORDER_WIDTH, p2t); // if inner area is empty, then make it big enough for at least the icon if (inner.IsEmpty()){ inner.SizeBy(2*(NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH,p2t)), 2*(NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH,p2t))); } // Make sure we have enough room to actually render the border within // our frame bounds if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) { return; } // Paint the border nsRecessedBorder recessedBorder(borderEdgeWidth); nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, inner, inner, recessedBorder, mStyleContext, 0); // Adjust the inner rect to account for the one pixel recessed border, // and a six pixel padding on each edge inner.Deflate(NSIntPixelsToTwips(ICON_PADDING+ALT_BORDER_WIDTH, p2t), NSIntPixelsToTwips(ICON_PADDING+ALT_BORDER_WIDTH, p2t)); if (inner.IsEmpty()) { return; } // check if the size of the (deflated) rect is big enough to show the icon // - if not, then just bail, leaving the border rect by itself // NOTE: setting the clip (below) should be enough, but there is a bug in the Linux // rendering context that images are not clipped when a clip rect is set (bugzilla bug 78497) // and also this will be slightly more efficient since we will not try to render any icons // or ALT text into the rect. if (inner.width < NSIntPixelsToTwips(ICON_SIZE, p2t) || inner.height < NSIntPixelsToTwips(ICON_SIZE, p2t)) { return; } // Clip so we don't render outside the inner rect PRBool clipState; aRenderingContext.PushState(); aRenderingContext.SetClipRect(inner, nsClipCombine_kIntersect, clipState); PRBool dispIcon = mIconLoad ? mIconLoad->mPrefShowPlaceholders : PR_TRUE; // Check if we should display image placeholders if (dispIcon) { PRInt32 size = NSIntPixelsToTwips(ICON_SIZE, p2t); PRBool iconUsed = PR_FALSE; // 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"); nsCOMPtr 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); aRenderingContext.DrawImage(imgCon, &r, &p); iconUsed = PR_TRUE; } } // 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()) { nsCOMPtr content; nsCOMPtr 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); } } } aRenderingContext.PopState(clipState); } NS_METHOD nsImageFrame::Paint(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer, PRUint32 aFlags) { PRBool isVisible; if (NS_SUCCEEDED(IsVisibleForPainting(aPresContext, aRenderingContext, PR_TRUE, &isVisible)) && isVisible && mRect.width && mRect.height) { // If painting is suppressed, we need to stop image painting. We // have to cover here because of input image controls. PRBool paintingSuppressed = PR_FALSE; nsCOMPtr shell; aPresContext->GetShell(getter_AddRefs(shell)); shell->IsPaintingSuppressed(&paintingSuppressed); if (paintingSuppressed) return NS_OK; // First paint background and borders, which should be in the // FOREGROUND or BACKGROUND paint layer if the element is // inline-level or block-level, respectively. (See CSS2 9.5, which // is the rationale for paint layers.) const nsStyleDisplay *display; ::GetStyleData(NS_STATIC_CAST(nsIStyleContext*, mStyleContext), &display); nsFramePaintLayer backgroundLayer = display->IsBlockLevel() ? NS_FRAME_PAINT_LAYER_BACKGROUND : NS_FRAME_PAINT_LAYER_FOREGROUND; if (aWhichLayer == backgroundLayer) { const nsStyleVisibility* vis = (const nsStyleVisibility*)mStyleContext->GetStyleData(eStyleStruct_Visibility); if (vis->IsVisibleOrCollapsed()) { const nsStyleBorder* myBorder = (const nsStyleBorder*) mStyleContext->GetStyleData(eStyleStruct_Border); nsRect rect(0, 0, mRect.width, mRect.height); nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this, aDirtyRect, rect, *myBorder, 0, 0); nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, aDirtyRect, rect, *myBorder, mStyleContext, 0); } } nsCOMPtr imgCon; PRUint32 loadStatus = imgIRequest::STATUS_ERROR; if (mLoads[0].mRequest) { mLoads[0].mRequest->GetImage(getter_AddRefs(imgCon)); mLoads[0].mRequest->GetImageStatus(&loadStatus); } if (loadStatus & imgIRequest::STATUS_ERROR || !imgCon) { // No image yet, or image load failed. Draw the alt-text and an icon // indicating the status (unless image is blocked, in which case we show nothing) #ifndef SUPPRESS_LOADING_ICON if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer && (!mImageBlocked || mIconLoad->mPrefAllImagesBlocked)) { #else if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer && mInitialLoadCompleted) { #endif DisplayAltFeedback(aPresContext, aRenderingContext, (loadStatus & imgIRequest::STATUS_ERROR) ? NS_ICON_BROKEN_IMAGE : NS_ICON_LOADING_IMAGE); } } else { PRBool paintOutline = PR_FALSE; mInitialLoadCompleted = PR_TRUE; if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) { // Now render the image into our content area (the area inside the // borders and padding) nsRect inner; GetInnerArea(aPresContext, inner); if (loadStatus & imgIRequest::STATUS_ERROR) { if (imgCon) { inner.SizeTo(mComputedSize); } } if (imgCon) { nscoord offsetY = 0; nsSize size(inner.width, inner.height); // if the image is split account for y-offset, border, padding if (mPrevInFlow || mNextInFlow) { if (mPrevInFlow) { offsetY = GetContinuationOffset(); } size.height = mRect.height; if (!mPrevInFlow) { size.height -= mBorderPadding.top; } if (!mNextInFlow) { size.height -= mBorderPadding.bottom; } } if (mLoads[0].mIntrinsicSize == mComputedSize) { inner.IntersectRect(inner, aDirtyRect); nsPoint p(inner.x, inner.y); nsRect r(inner.x, inner.y, inner.width, inner.height); r.x -= mBorderPadding.left; r.y -= mBorderPadding.top; if (mPrevInFlow) { r.y = offsetY; p.y = 0; } aRenderingContext.DrawImage(imgCon, &r, &p); } else { nsTransform2D trans; trans.SetToScale((float(mLoads[0].mIntrinsicSize.width) / float(inner.width)), (float(mLoads[0].mIntrinsicSize.height) / float(inner.height))); nsRect r(0, offsetY, size.width, size.height); trans.TransformCoord(&r.x, &r.y, &r.width, &r.height); nsRect d(inner.x, inner.y, mComputedSize.width, mComputedSize.height); // if the image is split account for y-offset, border, padding if (mPrevInFlow || mNextInFlow) { if (mPrevInFlow) { d.y -= mBorderPadding.top; } d.height = size.height; } aRenderingContext.DrawScaledImage(imgCon, &r, &d); } paintOutline = PR_TRUE; } } nsImageMap* map = GetImageMap(aPresContext); if (nsnull != map) { nsRect inner; GetInnerArea(aPresContext, inner); PRBool clipState; aRenderingContext.SetColor(NS_RGB(0, 0, 0)); aRenderingContext.SetLineStyle(nsLineStyle_kDotted); aRenderingContext.PushState(); aRenderingContext.Translate(inner.x, inner.y); map->Draw(aPresContext, aRenderingContext); aRenderingContext.PopState(clipState); paintOutline = PR_TRUE; } // paint the outline in the overlay layer (or if there is an image map) until the // general problem of painting it outside the border box is solved. if (paintOutline) { const nsStyleBorder* myBorder = (const nsStyleBorder*) mStyleContext->GetStyleData(eStyleStruct_Border); const nsStyleOutline* myOutline = (const nsStyleOutline*) mStyleContext->GetStyleData(eStyleStruct_Outline); nsRect rect(0, 0, mRect.width, mRect.height); nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this, aDirtyRect, rect, *myBorder, *myOutline, mStyleContext, 0); } #ifdef DEBUG if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) && GetShowFrameBorders()) { nsImageMap* map = GetImageMap(aPresContext); if (nsnull != map) { nsRect inner; GetInnerArea(aPresContext, inner); PRBool clipState; aRenderingContext.SetColor(NS_RGB(0, 0, 0)); aRenderingContext.PushState(); aRenderingContext.Translate(inner.x, inner.y); map->Draw(aPresContext, aRenderingContext); aRenderingContext.PopState(clipState); } } #endif } } PRInt16 displaySelection = 0; nsresult result; nsCOMPtr shell; result = aPresContext->GetShell(getter_AddRefs(shell)); if (NS_FAILED(result)) return result; result = shell->GetSelectionFlags(&displaySelection); if (NS_FAILED(result)) return result; if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES)) return NS_OK;//no need to check the blue border, we cannot be drawn selected return nsFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer, nsISelectionDisplay::DISPLAY_IMAGES); } NS_IMETHODIMP nsImageFrame::GetImageMap(nsIPresContext *aPresContext, nsIImageMap **aImageMap) { nsImageMap *map = GetImageMap(aPresContext); return map->QueryInterface(NS_GET_IID(nsIImageMap), (void**)aImageMap); } nsImageMap* nsImageFrame::GetImageMap(nsIPresContext* aPresContext) { if (!mImageMap) { nsCOMPtr doc; mContent->GetDocument(*getter_AddRefs(doc)); if (!doc) { return nsnull; } nsAutoString usemap; mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::usemap, usemap); nsCOMPtr map; if (NS_SUCCEEDED(nsImageMapUtils::FindImageMap(doc,usemap,getter_AddRefs(map))) && map) { nsCOMPtr presShell; aPresContext->GetShell(getter_AddRefs(presShell)); mImageMap = new nsImageMap(); if (mImageMap) { NS_ADDREF(mImageMap); mImageMap->Init(presShell, this, map); } } } return mImageMap; } void nsImageFrame::TriggerLink(nsIPresContext* aPresContext, const nsString& aURLSpec, const nsString& aTargetSpec, PRBool aClick) { // We get here with server side image map nsILinkHandler* handler = nsnull; aPresContext->GetLinkHandler(&handler); if (nsnull != handler) { if (aClick) { nsresult proceed = NS_OK; // Check that this page is allowed to load this URI. // Almost a copy of the similarly named method in nsGenericElement nsresult rv; nsCOMPtr securityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); nsCOMPtr ps; if (NS_SUCCEEDED(rv)) rv = aPresContext->GetShell(getter_AddRefs(ps)); nsCOMPtr doc; if (NS_SUCCEEDED(rv) && ps) rv = ps->GetDocument(getter_AddRefs(doc)); nsCOMPtr baseURI; if (NS_SUCCEEDED(rv) && doc) doc->GetDocumentURL(getter_AddRefs(baseURI)); nsCOMPtr absURI; if (NS_SUCCEEDED(rv)) rv = NS_NewURI(getter_AddRefs(absURI), aURLSpec, nsnull, baseURI); if (NS_SUCCEEDED(rv)) proceed = securityManager->CheckLoadURI(baseURI, absURI, nsIScriptSecurityManager::STANDARD); // Only pass off the click event if the script security manager // says it's ok. if (NS_SUCCEEDED(proceed)) handler->OnLinkClick(mContent, eLinkVerb_Replace, aURLSpec.get(), aTargetSpec.get()); } else { handler->OnOverLink(mContent, aURLSpec.get(), aTargetSpec.get()); } NS_RELEASE(handler); } } PRBool nsImageFrame::IsServerImageMap() { nsAutoString ismap; return NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::ismap, ismap); } PRIntn nsImageFrame::GetSuppress() { nsAutoString s; if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::suppress, s)) { if (s.EqualsIgnoreCase("true")) { return SUPPRESS; } else if (s.EqualsIgnoreCase("false")) { return DONT_SUPPRESS; } } return DEFAULT_SUPPRESS; } //XXX the event come's in in view relative coords, but really should //be in frame relative coords by the time it hits our frame. // Translate an point that is relative to our view (or a containing // view) into a localized pixel coordinate that is relative to the // content area of this frame (inside the border+padding). void nsImageFrame::TranslateEventCoords(nsIPresContext* aPresContext, const nsPoint& aPoint, nsPoint& aResult) { nscoord x = aPoint.x; nscoord y = aPoint.y; // If we have a view then the event coordinates are already relative // to this frame; otherwise we have to adjust the coordinates // appropriately. nsIView* view; GetView(aPresContext, &view); if (nsnull == view) { nsPoint offset; GetOffsetFromView(aPresContext, offset, &view); if (nsnull != view) { x -= offset.x; y -= offset.y; } } // Subtract out border and padding here so that the coordinates are // now relative to the content area of this frame. nsRect inner; GetInnerArea(aPresContext, inner); x -= inner.x; y -= inner.y; // Translate the coordinates from twips to pixels float t2p; aPresContext->GetTwipsToPixels(&t2p); aResult.x = NSTwipsToIntPixels(x, t2p); aResult.y = NSTwipsToIntPixels(y, t2p); } PRBool nsImageFrame::GetAnchorHREFAndTarget(nsString& aHref, nsString& aTarget) { PRBool status = PR_FALSE; aHref.Truncate(); aTarget.Truncate(); // Walk up the content tree, looking for an nsIDOMAnchorElement nsCOMPtr content; mContent->GetParent(*getter_AddRefs(content)); while (content) { nsCOMPtr anchor(do_QueryInterface(content)); if (anchor) { anchor->GetHref(aHref); if (aHref.Length() > 0) { status = PR_TRUE; } anchor->GetTarget(aTarget); break; } nsCOMPtr parent; content->GetParent(*getter_AddRefs(parent)); content = parent; } return status; } NS_IMETHODIMP nsImageFrame::CanContinueTextRun(PRBool& aContinueTextRun) const { // images really CAN continue text runs, but the textFrame needs to be // educated before we can indicate that it can. For now, we handle the fixing up // of max element widths in nsLineLayout::VerticalAlignFrames, but hopefully // this can be eliminated and the textFrame can be convinced to handle inlines // that take up space in text runs. aContinueTextRun = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsImageFrame::GetContentForEvent(nsIPresContext* aPresContext, nsEvent* aEvent, nsIContent** aContent) { NS_ENSURE_ARG_POINTER(aContent); nsImageMap* map; map = GetImageMap(aPresContext); if (nsnull != map) { nsPoint p; TranslateEventCoords(aPresContext, aEvent->point, p); nsAutoString absURL, target, altText; PRBool suppress; PRBool inside = PR_FALSE; nsCOMPtr area; inside = map->IsInside(p.x, p.y, getter_AddRefs(area), absURL, target, altText, &suppress); if (inside && area) { *aContent = area; NS_ADDREF(*aContent); return NS_OK; } } return GetContent(aContent); } // XXX what should clicks on transparent pixels do? NS_METHOD nsImageFrame::HandleEvent(nsIPresContext* aPresContext, nsGUIEvent* aEvent, nsEventStatus* aEventStatus) { NS_ENSURE_ARG_POINTER(aEventStatus); nsImageMap* map; switch (aEvent->message) { case NS_MOUSE_LEFT_BUTTON_UP: case NS_MOUSE_MOVE: { map = GetImageMap(aPresContext); PRBool isServerMap = IsServerImageMap(); if ((nsnull != map) || isServerMap) { nsPoint p; TranslateEventCoords(aPresContext, aEvent->point, p); nsAutoString absURL, target, altText; PRBool suppress; PRBool inside = PR_FALSE; // Even though client-side image map triggering happens // through content, we need to make sure we're not inside // (in case we deal with a case of both client-side and // sever-side on the same image - it happens!) if (nsnull != map) { nsCOMPtr area; inside = map->IsInside(p.x, p.y, getter_AddRefs(area), absURL, target, altText, &suppress); } if (!inside && isServerMap) { suppress = GetSuppress(); nsCOMPtr baseURL; GetBaseURI(getter_AddRefs(baseURL)); if (baseURL) { // Server side image maps use the href in a containing anchor // element to provide the basis for the destination url. nsAutoString src; if (GetAnchorHREFAndTarget(src, target)) { NS_MakeAbsoluteURI(absURL, src, baseURL); // XXX if the mouse is over/clicked in the border/padding area // we should probably just pretend nothing happened. Nav4 // keeps the x,y coordinates positive as we do; IE doesn't // bother. Both of them send the click through even when the // mouse is over the border. if (p.x < 0) p.x = 0; if (p.y < 0) p.y = 0; char cbuf[50]; PR_snprintf(cbuf, sizeof(cbuf), "?%d,%d", p.x, p.y); absURL.AppendWithConversion(cbuf); PRBool clicked = PR_FALSE; if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { *aEventStatus = nsEventStatus_eConsumeDoDefault; clicked = PR_TRUE; } TriggerLink(aPresContext, absURL, target, clicked); } } else { NS_WARNING("baseURL is null for imageFrame - ignoring mouse event"); } } } break; } default: break; } return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus); } //XXX This will need to be rewritten once we have content for areas NS_METHOD nsImageFrame::GetCursor(nsIPresContext* aPresContext, nsPoint& aPoint, PRInt32& aCursor) { nsImageMap* map = GetImageMap(aPresContext); if (nsnull != map) { nsPoint p; TranslateEventCoords(aPresContext, aPoint, p); aCursor = NS_STYLE_CURSOR_DEFAULT; if (map->IsInside(p.x, p.y)) { // Use style defined cursor if one is provided, otherwise when // the cursor style is "auto" we use the pointer cursor. const nsStyleUserInterface* styleUserInterface; GetStyleData(eStyleStruct_UserInterface, (const nsStyleStruct*&)styleUserInterface); aCursor = styleUserInterface->mCursor; if (NS_STYLE_CURSOR_AUTO == aCursor) { aCursor = NS_STYLE_CURSOR_POINTER; } } return NS_OK; } return nsFrame::GetCursor(aPresContext, aPoint, aCursor); } NS_IMETHODIMP nsImageFrame::AttributeChanged(nsIPresContext* aPresContext, nsIContent* aChild, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType, PRInt32 aHint) { nsresult rv = nsSplittableFrame::AttributeChanged(aPresContext, aChild, aNameSpaceID, aAttribute, aModType, aHint); if (NS_OK != rv) { return rv; } if (nsHTMLAtoms::src == aAttribute) { nsAutoString newSRC; aChild->GetAttr(kNameSpaceID_None, nsHTMLAtoms::src, newSRC); if (mLoads[0].mRequest) { PRUint32 loadStatus = imgIRequest::STATUS_ERROR; mLoads[0].mRequest->GetImageStatus(&loadStatus); if (!(loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) { mFailureReplace = PR_FALSE; // don't cause a CantRenderReplacedElement call mLoads[0].mRequest->Cancel(NS_ERROR_FAILURE); mLoads[0].mRequest = nsnull; } } mCanSendLoadEvent = PR_TRUE; if (mLoads[1].mRequest) { mLoads[1].mRequest->Cancel(NS_ERROR_FAILURE); mLoads[1].mRequest = nsnull; } nsCOMPtr req(do_CreateInstance("@mozilla.org/image/request;1")); if (!mLoads[0].mRequest) { mLoads[0].mRequest = req; } else { mLoads[1].mRequest = req; } LoadImage(newSRC, aPresContext, req); } else if (nsHTMLAtoms::width == aAttribute || nsHTMLAtoms::height == aAttribute) { // XXX: could check for new width == old width, and make that a no-op nsCOMPtr presShell; aPresContext->GetShell(getter_AddRefs(presShell)); mState |= NS_FRAME_IS_DIRTY; mParent->ReflowDirtyChild(presShell, (nsIFrame*) this); // NOTE: mSizeFixed will be updated in Reflow... } return NS_OK; } NS_IMETHODIMP nsImageFrame::GetFrameType(nsIAtom** aType) const { NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer"); *aType = nsLayoutAtoms::imageFrame; NS_ADDREF(*aType); return NS_OK; } #ifdef DEBUG NS_IMETHODIMP nsImageFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const { IndentBy(out, aIndent); ListTag(out); #ifdef DEBUG_waterson fprintf(out, " [parent=%p]", mParent); #endif nsIView* view; GetView(aPresContext, &view); if (view) { fprintf(out, " [view=%p]", view); } fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); if (0 != mState) { fprintf(out, " [state=%08x]", mState); } fprintf(out, " [content=%p]", mContent); // output the img src url if (mLoads[0].mRequest) { nsCOMPtr uri; mLoads[0].mRequest->GetURI(getter_AddRefs(uri)); nsCAutoString uristr; uri->GetAsciiSpec(uristr); fprintf(out, " [src=%s]", uristr.get()); } fputs("\n", out); return NS_OK; } #endif NS_IMETHODIMP nsImageFrame::GetIntrinsicImageSize(nsSize& aSize) { aSize = mLoads[0].mIntrinsicSize; return NS_OK; } NS_IMETHODIMP nsImageFrame::GetNaturalImageSize(PRUint32 *naturalWidth, PRUint32 *naturalHeight) { *naturalWidth = 0; *naturalHeight = 0; if (mLoads[0].mRequest) { nsCOMPtr container; mLoads[0].mRequest->GetImage(getter_AddRefs(container)); if (container) { PRInt32 w, h; container->GetWidth(&w); container->GetHeight(&h); *naturalWidth = NS_STATIC_CAST(PRUint32, w); *naturalHeight = NS_STATIC_CAST(PRUint32, h); } } return NS_OK; } NS_IMETHODIMP nsImageFrame::GetImageRequest(imgIRequest **aRequest) { *aRequest = mLoads[0].mRequest; NS_IF_ADDREF(*aRequest); return NS_OK; } NS_IMETHODIMP nsImageFrame::IsImageComplete(PRBool* aComplete) { NS_ENSURE_ARG_POINTER(aComplete); if (!mLoads[0].mRequest) { *aComplete = PR_FALSE; return NS_OK; } PRUint32 status; mLoads[0].mRequest->GetImageStatus(&status); *aComplete = ((status & imgIRequest::STATUS_LOAD_COMPLETE) != 0); return NS_OK; } nsresult nsImageFrame::LoadImage(const nsAString& aSpec, nsIPresContext *aPresContext, imgIRequest *aRequest, PRBool aCheckContentPolicy) { nsresult rv = RealLoadImage(aSpec, aPresContext, aRequest, aCheckContentPolicy); if (NS_FAILED(rv)) { int whichLoad = GetImageLoad(aRequest); if (whichLoad == -1) return NS_ERROR_FAILURE; mLoads[whichLoad].mRequest = nsnull; } return rv; } nsresult nsImageFrame::RealLoadImage(const nsAString& aSpec, nsIPresContext *aPresContext, imgIRequest *aRequest, PRBool aCheckContentPolicy) { nsresult rv = NS_OK; /* set this to TRUE here in case we return early */ mInitialLoadCompleted = PR_TRUE; /* don't load the image if aSpec is empty */ if (aSpec.IsEmpty()) return NS_ERROR_FAILURE; nsCOMPtr realURI; /* don't load the image if some security check fails... */ GetRealURI(aSpec, getter_AddRefs(realURI)); if (aCheckContentPolicy) if (!CanLoadImage(realURI)) return NS_ERROR_FAILURE; if (!mListener) { nsImageListener *listener = new nsImageListener(this); if (!listener) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(listener); listener->QueryInterface(NS_GET_IID(imgIDecoderObserver), getter_AddRefs(mListener)); NS_ASSERTION(mListener, "queryinterface for the listener failed"); NS_RELEASE(listener); } nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1", &rv)); if (NS_FAILED(rv)) return rv; nsCOMPtr loadGroup; GetLoadGroup(aPresContext, getter_AddRefs(loadGroup)); nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL; if (aPresContext) { aPresContext->GetImageLoadFlags(loadFlags); } /* get the URI, convert internal-gopher-stuff if needed */ nsCOMPtr uri; GetURI(aSpec, getter_AddRefs(uri)); if (!uri) { if (!realURI) return NS_ERROR_FAILURE; uri = realURI; } /* set this back to FALSE before we do the real load */ mInitialLoadCompleted = PR_FALSE; nsCOMPtr baseURI; rv = aPresContext->GetBaseURL(getter_AddRefs(baseURI)); // Get the document URI for the referrer... nsCOMPtr documentURI; nsCOMPtr doc; if (mContent) { (void) mContent->GetDocument(*getter_AddRefs(doc)); if (doc) { doc->GetDocumentURL(getter_AddRefs(documentURI)); } } nsCOMPtr tempRequest; return il->LoadImage(uri, baseURI, documentURI, loadGroup, mListener, aPresContext, loadFlags, nsnull, aRequest, getter_AddRefs(tempRequest)); } #define INTERNAL_GOPHER_LENGTH 16 /* "internal-gopher-" length */ void nsImageFrame::GetDocumentCharacterSet(nsAString& aCharset) const { nsresult rv; nsCOMPtr htmlContent(do_QueryInterface(mContent, &rv)); if (NS_SUCCEEDED(rv)) { nsCOMPtr doc; rv = htmlContent->GetDocument(*getter_AddRefs(doc)); if (NS_SUCCEEDED(rv)) (void) doc->GetDocumentCharacterSet(aCharset); } } void nsImageFrame::GetURI(const nsAString& aSpec, nsIURI **aURI) { *aURI = nsnull; /* Note: navigator 4.* and earlier releases ignored the base tags effect on the builtin images. So we do too. Use aSpec instead of the absolute url... */ /* The prefix for special "internal" images that are well known. Look and see if this is an internal-gopher- url. */ if (NS_LITERAL_STRING("internal-gopher-").Equals(Substring(aSpec, 0, INTERNAL_GOPHER_LENGTH))) { nsAutoString newURI; newURI.Assign(NS_LITERAL_STRING("resource:/res/html/gopher-") + Substring(aSpec, INTERNAL_GOPHER_LENGTH, aSpec.Length() - INTERNAL_GOPHER_LENGTH) + NS_LITERAL_STRING(".gif")); GetRealURI(newURI, aURI); } } void nsImageFrame::GetRealURI(const nsAString& aSpec, nsIURI **aURI) { nsCOMPtr baseURI; GetBaseURI(getter_AddRefs(baseURI)); nsAutoString charset; GetDocumentCharacterSet(charset); NS_NewURI(aURI, aSpec, charset.IsEmpty() ? nsnull : NS_ConvertUCS2toUTF8(charset).get(), baseURI); } void nsImageFrame::GetBaseURI(nsIURI **aURI) { NS_PRECONDITION(nsnull != aURI, "null OUT parameter pointer"); nsresult rv; nsCOMPtr baseURI; nsCOMPtr htmlContent(do_QueryInterface(mContent, &rv)); if (NS_SUCCEEDED(rv)) { htmlContent->GetBaseURL(*getter_AddRefs(baseURI)); } else { nsCOMPtr doc; if (mContent) { rv = mContent->GetDocument(*getter_AddRefs(doc)); if (doc) { doc->GetBaseURL(*getter_AddRefs(baseURI)); } } } *aURI = baseURI; NS_IF_ADDREF(*aURI); } void nsImageFrame::GetLoadGroup(nsIPresContext *aPresContext, nsILoadGroup **aLoadGroup) { if (!aPresContext) return; NS_PRECONDITION(nsnull != aLoadGroup, "null OUT parameter pointer"); nsCOMPtr shell; aPresContext->GetShell(getter_AddRefs(shell)); if (!shell) return; nsCOMPtr doc; shell->GetDocument(getter_AddRefs(doc)); if (!doc) return; doc->GetDocumentLoadGroup(aLoadGroup); } PRBool nsImageFrame::CanLoadImage(nsIURI *aURI) { PRBool shouldLoad = PR_TRUE; // default permit // Check with the content-policy things to make sure this load is permitted. nsresult rv; nsCOMPtr element(do_QueryInterface(mContent)); if (!element) // this would seem bad(tm) return shouldLoad; nsCOMPtr document; if (mContent) { rv = mContent->GetDocument(*getter_AddRefs(document)); if (! document) { NS_ASSERTION(0, "expecting a document"); return shouldLoad; } nsCOMPtr globalScript; rv = document->GetScriptGlobalObject(getter_AddRefs(globalScript)); if (NS_FAILED(rv)) return shouldLoad; nsCOMPtr domWin(do_QueryInterface(globalScript)); rv = NS_CheckContentLoadPolicy(nsIContentPolicy::IMAGE, aURI, element, domWin, &shouldLoad); if (NS_SUCCEEDED(rv) && !shouldLoad) { // this image has been blocked, so flag it mImageBlocked = PR_TRUE; return shouldLoad; } } /* ... additional checks ? */ return shouldLoad; } nsresult nsImageFrame::LoadIcons(nsIPresContext *aPresContext) { NS_NAMED_LITERAL_STRING(loadingSrc,"resource:/res/loading-image.gif"); NS_NAMED_LITERAL_STRING(brokenSrc,"resource:/res/broken-image.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, PR_FALSE); 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, PR_FALSE); // 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) { // invalidate the inner area, where the icon lives 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)); NS_ASSERTION(!rect.IsEmpty(), "icon rect cannot be empty!"); Invalidate(aPresContext, rect, PR_FALSE); } void nsImageFrame::IconLoad::GetPrefs(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 prefs = do_GetService(NS_PREF_CONTRACTID); if (prefs) { PRBool boolPref; PRInt32 intPref; if (NS_SUCCEEDED(prefs->GetBoolPref("browser.display.force_inline_alttext", &boolPref))) { mPrefForceInlineAltText = boolPref; } else { mPrefForceInlineAltText = PR_FALSE; } if (NS_SUCCEEDED(prefs->GetIntPref("network.image.imageBehavior", &intPref)) && intPref == 2) { mPrefAllImagesBlocked = PR_TRUE; } else { mPrefAllImagesBlocked = PR_FALSE; } if (NS_SUCCEEDED(prefs->GetBoolPref("browser.display.show_image_placeholders", &boolPref))) { mPrefShowPlaceholders = boolPref; } else { mPrefShowPlaceholders = PR_TRUE; } } } #ifdef DEBUG NS_IMETHODIMP nsImageFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { #if 0 if (!aResult) { return NS_ERROR_NULL_POINTER; } PRUint32 sum; mImageLoader.SizeOf(aHandler, &sum); sum += sizeof(*this) - sizeof(mImageLoader); if (mImageMap) { PRBool recorded; aHandler->RecordObject((void*) mImageMap, &recorded); if (!recorded) { PRUint32 mapSize; mImageMap->SizeOf(aHandler, &mapSize); aHandler->AddSize(nsLayoutAtoms::imageMap, mapSize); } } *aResult = sum; #endif *aResult = 0; return NS_OK; } #endif NS_IMPL_ISUPPORTS2(nsImageListener, imgIDecoderObserver, imgIContainerObserver) nsImageListener::nsImageListener(nsImageFrame *aFrame) : mFrame(aFrame) { NS_INIT_ISUPPORTS(); } nsImageListener::~nsImageListener() { } NS_IMETHODIMP nsImageListener::OnStartDecode(imgIRequest *aRequest, nsISupports *aContext) { if (!mFrame) return NS_ERROR_FAILURE; nsCOMPtr pc(do_QueryInterface(aContext)); NS_ASSERTION(pc, "not a pres context!"); return mFrame->OnStartDecode(aRequest, pc); } NS_IMETHODIMP nsImageListener::OnStartContainer(imgIRequest *aRequest, nsISupports *aContext, imgIContainer *aImage) { if (!mFrame) return NS_ERROR_FAILURE; nsCOMPtr pc(do_QueryInterface(aContext)); NS_ASSERTION(pc, "not a pres context!"); return mFrame->OnStartContainer(aRequest, pc, aImage); } NS_IMETHODIMP nsImageListener::OnStartFrame(imgIRequest *aRequest, nsISupports *aContext, gfxIImageFrame *aFrame) { if (!mFrame) return NS_ERROR_FAILURE; nsCOMPtr pc(do_QueryInterface(aContext)); NS_ASSERTION(pc, "not a pres context!"); return mFrame->OnStartFrame(aRequest, pc, aFrame); } NS_IMETHODIMP nsImageListener::OnDataAvailable(imgIRequest *aRequest, nsISupports *aContext, gfxIImageFrame *aFrame, const nsRect *aRect) { if (!mFrame) return NS_ERROR_FAILURE; nsCOMPtr pc(do_QueryInterface(aContext)); NS_ASSERTION(pc, "not a pres context!"); return mFrame->OnDataAvailable(aRequest, pc, aFrame, aRect); } NS_IMETHODIMP nsImageListener::OnStopFrame(imgIRequest *aRequest, nsISupports *aContext, gfxIImageFrame *aFrame) { if (!mFrame) return NS_ERROR_FAILURE; nsCOMPtr pc(do_QueryInterface(aContext)); NS_ASSERTION(pc, "not a pres context!"); return mFrame->OnStopFrame(aRequest, pc, aFrame); } NS_IMETHODIMP nsImageListener::OnStopContainer(imgIRequest *aRequest, nsISupports *aContext, imgIContainer *aImage) { if (!mFrame) return NS_ERROR_FAILURE; nsCOMPtr pc(do_QueryInterface(aContext)); NS_ASSERTION(pc, "not a pres context!"); return mFrame->OnStopContainer(aRequest, pc, aImage); } NS_IMETHODIMP nsImageListener::OnStopDecode(imgIRequest *aRequest, nsISupports *aContext, nsresult status, const PRUnichar *statusArg) { if (!mFrame) return NS_ERROR_FAILURE; nsCOMPtr pc(do_QueryInterface(aContext)); NS_ASSERTION(pc, "not a pres context!"); return mFrame->OnStopDecode(aRequest, pc, status, statusArg); } NS_IMETHODIMP nsImageListener::FrameChanged(imgIContainer *aContainer, nsISupports *aContext, gfxIImageFrame *newframe, nsRect * dirtyRect) { if (!mFrame) return NS_ERROR_FAILURE; nsCOMPtr pc(do_QueryInterface(aContext)); NS_ASSERTION(pc, "not a pres context!"); return mFrame->FrameChanged(aContainer, pc, newframe, dirtyRect); }