Back to index

lightning-sunbird  0.9+nobinonly
nsImageFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsHTMLParts.h"
00038 #include "nsCOMPtr.h"
00039 #include "nsImageFrame.h"
00040 #include "nsIImageLoadingContent.h"
00041 #include "nsString.h"
00042 #include "nsPrintfCString.h"
00043 #include "nsPresContext.h"
00044 #include "nsIRenderingContext.h"
00045 #include "nsIPresShell.h"
00046 #include "nsIImage.h"
00047 #include "nsIWidget.h"
00048 #include "nsHTMLAtoms.h"
00049 #include "nsIDocument.h"
00050 #include "nsINodeInfo.h"
00051 #include "nsContentUtils.h"
00052 #include "nsStyleContext.h"
00053 #include "nsStyleConsts.h"
00054 #include "nsImageMap.h"
00055 #include "nsILinkHandler.h"
00056 #include "nsIURL.h"
00057 #include "nsIIOService.h"
00058 #include "nsIURL.h"
00059 #include "nsILoadGroup.h"
00060 #include "nsISupportsPriority.h"
00061 #include "nsIServiceManager.h"
00062 #include "nsNetUtil.h"
00063 #include "nsIView.h"
00064 #include "nsIViewManager.h"
00065 #include "nsHTMLContainerFrame.h"
00066 #include "prprf.h"
00067 #include "nsIFontMetrics.h"
00068 #include "nsCSSRendering.h"
00069 #include "nsILink.h"
00070 #include "nsIDOMHTMLAnchorElement.h"
00071 #include "nsIDOMHTMLImageElement.h"
00072 #include "nsIDeviceContext.h"
00073 #include "nsINameSpaceManager.h"
00074 #include "nsTextFragment.h"
00075 #include "nsIDOMHTMLMapElement.h"
00076 #include "nsLayoutAtoms.h"
00077 #include "nsImageMapUtils.h"
00078 #include "nsIScriptSecurityManager.h"
00079 #ifdef ACCESSIBILITY
00080 #include "nsIAccessibilityService.h"
00081 #endif
00082 #include "nsIServiceManager.h"
00083 #include "nsIDOMNode.h"
00084 #include "nsGUIEvent.h"
00085 #include "nsStyleSet.h"
00086 
00087 #include "imgIContainer.h"
00088 #include "imgILoader.h"
00089 
00090 #include "nsIEventQueueService.h"
00091 #include "plevent.h"
00092 
00093 #include "nsContentPolicyUtils.h"
00094 #include "nsIScriptGlobalObject.h"
00095 #include "nsIDOMWindow.h"
00096 #include "nsIDOMDocument.h"
00097 #include "nsCSSFrameConstructor.h"
00098 #include "nsIPrefBranch2.h"
00099 #include "nsIPrefService.h"
00100 #include "gfxIImageFrame.h"
00101 #include "nsIDOMRange.h"
00102 
00103 #include "nsIContentPolicy.h"
00104 #include "nsContentPolicyUtils.h"
00105 
00106 #include "nsLayoutErrors.h"
00107 
00108 #ifdef DEBUG
00109 #undef NOISY_IMAGE_LOADING
00110 #undef NOISY_ICON_LOADING
00111 #else
00112 #undef NOISY_IMAGE_LOADING
00113 #undef NOISY_ICON_LOADING
00114 #endif
00115 
00116 // sizes (pixels) for image icon, padding and border frame
00117 #define ICON_SIZE        (16)
00118 #define ICON_PADDING     (3)
00119 #define ALT_BORDER_WIDTH (1)
00120 
00121 
00122 //we must add hooks soon
00123 #define IMAGE_EDITOR_CHECK 1
00124 
00125 // Default alignment value (so we can tell an unset value from a set value)
00126 #define ALIGN_UNSET PRUint8(-1)
00127 
00128 // static icon information
00129 nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nsnull;
00130 
00131 // cached IO service for loading icons
00132 nsIIOService* nsImageFrame::sIOService;
00133 
00134 // test if the width and height are fixed, looking at the style data
00135 static PRBool HaveFixedSize(const nsStylePosition* aStylePosition)
00136 {
00137   // check the width and height values in the reflow state's style struct
00138   // - if width and height are specified as either coord or percentage, then
00139   //   the size of the image frame is constrained
00140   nsStyleUnit widthUnit = aStylePosition->mWidth.GetUnit();
00141   nsStyleUnit heightUnit = aStylePosition->mHeight.GetUnit();
00142 
00143   return ((widthUnit  == eStyleUnit_Coord ||
00144            widthUnit  == eStyleUnit_Percent) &&
00145           (heightUnit == eStyleUnit_Coord ||
00146            heightUnit == eStyleUnit_Percent));
00147 }
00148 // use the data in the reflow state to decide if the image has a constrained size
00149 // (i.e. width and height that are based on the containing block size and not the image size) 
00150 // so we can avoid animated GIF related reflows
00151 inline PRBool HaveFixedSize(const nsHTMLReflowState& aReflowState)
00152 { 
00153   NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition");
00154   // when an image has percent css style height or width, but mComputedHeight 
00155   // or mComputedWidth of reflow state is  NS_UNCONSTRAINEDSIZE  
00156   // it needs to return PR_FALSE to cause an incremental reflow later
00157   // if an image is inside table like bug 156731 simple testcase III, 
00158   // during pass 1 reflow, mComputedWidth is NS_UNCONSTRAINEDSIZE
00159   // in pass 2 reflow, mComputedWidth is 0, it also needs to return PR_FALSE
00160   // see bug 156731
00161   nsStyleUnit heightUnit = (*(aReflowState.mStylePosition)).mHeight.GetUnit();
00162   nsStyleUnit widthUnit = (*(aReflowState.mStylePosition)).mWidth.GetUnit();
00163   return ((eStyleUnit_Percent == heightUnit && NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) ||
00164           (eStyleUnit_Percent == widthUnit && (NS_UNCONSTRAINEDSIZE == aReflowState.mComputedWidth ||
00165            0 == aReflowState.mComputedWidth)))
00166           ? PR_FALSE
00167           : HaveFixedSize(aReflowState.mStylePosition); 
00168 }
00169 
00170 nsresult
00171 NS_NewImageFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00172 {
00173   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00174   if (nsnull == aNewFrame) {
00175     return NS_ERROR_NULL_POINTER;
00176   }
00177   nsImageFrame* it = new (aPresShell) nsImageFrame;
00178   if (nsnull == it) {
00179     return NS_ERROR_OUT_OF_MEMORY;
00180   }
00181   *aNewFrame = it;
00182   return NS_OK;
00183 }
00184 
00185 
00186 nsImageFrame::nsImageFrame() :
00187   mComputedSize(0, 0),
00188   mIntrinsicSize(0, 0)
00189 {
00190   // We assume our size is not constrained and we haven't gotten an
00191   // initial reflow yet, so don't touch those flags.
00192 }
00193 
00194 nsImageFrame::~nsImageFrame()
00195 {
00196 }
00197 
00198 NS_IMETHODIMP
00199 nsImageFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
00200 {
00201   NS_ENSURE_ARG_POINTER(aInstancePtr);
00202   *aInstancePtr = nsnull;
00203 
00204 #ifdef DEBUG
00205   if (aIID.Equals(NS_GET_IID(nsIFrameDebug))) {
00206     *aInstancePtr = NS_STATIC_CAST(nsIFrameDebug*,this);
00207     return NS_OK;
00208   }
00209 #endif
00210 
00211   if (aIID.Equals(NS_GET_IID(nsIImageFrame))) {
00212     *aInstancePtr = NS_STATIC_CAST(nsIImageFrame*,this);
00213     return NS_OK;
00214   } else if (aIID.Equals(NS_GET_IID(nsIFrame))) {
00215     *aInstancePtr = NS_STATIC_CAST(nsIFrame*,this);
00216     return NS_OK;
00217   } else if (aIID.Equals(NS_GET_IID(nsISupports))) {
00218     *aInstancePtr = NS_STATIC_CAST(nsIImageFrame*,this);
00219     return NS_OK;
00220   }
00221 
00222   return NS_NOINTERFACE;
00223 }
00224 
00225 #ifdef ACCESSIBILITY
00226 NS_IMETHODIMP nsImageFrame::GetAccessible(nsIAccessible** aAccessible)
00227 {
00228   nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
00229 
00230   if (accService) {
00231     return accService->CreateHTMLImageAccessible(NS_STATIC_CAST(nsIFrame*, this), aAccessible);
00232   }
00233 
00234   return NS_ERROR_FAILURE;
00235 }
00236 #endif
00237 
00238 NS_IMETHODIMP_(nsrefcnt) nsImageFrame::AddRef(void)
00239 {
00240   NS_WARNING("not supported for frames");
00241   return 1;
00242 }
00243 
00244 NS_IMETHODIMP_(nsrefcnt) nsImageFrame::Release(void)
00245 {
00246   NS_WARNING("not supported for frames");
00247   return 1;
00248 }
00249 
00250 NS_IMETHODIMP
00251 nsImageFrame::Destroy(nsPresContext* aPresContext)
00252 {
00253   // Tell our image map, if there is one, to clean up
00254   // This causes the nsImageMap to unregister itself as
00255   // a DOM listener.
00256   if (mImageMap) {
00257     mImageMap->Destroy();
00258     NS_RELEASE(mImageMap);
00259   }
00260 
00261   // set the frame to null so we don't send messages to a dead object.
00262   if (mListener) {
00263     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
00264     if (imageLoader) {
00265       imageLoader->RemoveObserver(mListener);
00266     }
00267     
00268     NS_REINTERPRET_CAST(nsImageListener*, mListener.get())->SetFrame(nsnull);
00269   }
00270   
00271   mListener = nsnull;
00272 
00273   return nsSplittableFrame::Destroy(aPresContext);
00274 }
00275 
00276 
00277 
00278 NS_IMETHODIMP
00279 nsImageFrame::Init(nsPresContext*  aPresContext,
00280                    nsIContent*      aContent,
00281                    nsIFrame*        aParent,
00282                    nsStyleContext*  aContext,
00283                    nsIFrame*        aPrevInFlow)
00284 {
00285   nsresult  rv = nsSplittableFrame::Init(aPresContext, aContent, aParent,
00286                                          aContext, aPrevInFlow);
00287   NS_ENSURE_SUCCESS(rv, rv);
00288 
00289   mListener = new nsImageListener(this);
00290   if (!mListener) return NS_ERROR_OUT_OF_MEMORY;
00291 
00292   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
00293   NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
00294   imageLoader->AddObserver(mListener);
00295 
00296   if (!gIconLoad)
00297     LoadIcons(aPresContext);
00298 
00299   nsCOMPtr<imgIRequest> currentRequest;
00300   imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
00301                           getter_AddRefs(currentRequest));
00302   PRUint32 currentLoadStatus = imgIRequest::STATUS_ERROR;
00303   if (currentRequest) {
00304     currentRequest->GetImageStatus(&currentLoadStatus);
00305 
00306     // Give image loads associated with an image frame a small priority boost!
00307     nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
00308     if (p)
00309       p->AdjustPriority(-1);
00310   }
00311 
00312   if (currentLoadStatus & imgIRequest::STATUS_ERROR) {
00313     PRInt16 imageStatus = nsIContentPolicy::ACCEPT;
00314     imageLoader->GetImageBlockingStatus(&imageStatus);
00315     rv = HandleLoadError(imageStatus);
00316   }
00317   // If we already have an image container, OnStartContainer won't be called
00318   // Set the animation mode here
00319   if (currentRequest) {
00320     nsCOMPtr<imgIContainer> image;
00321     currentRequest->GetImage(getter_AddRefs(image));
00322     if (image) {
00323       image->SetAnimationMode(aPresContext->ImageAnimationMode());
00324       // Ensure the animation (if any) is started.
00325       image->StartAnimation();
00326     }
00327   }
00328 
00329   return rv;
00330 }
00331 
00332 PRBool
00333 nsImageFrame::RecalculateTransform(imgIContainer* aImage)
00334 {
00335   PRBool intrinsicSizeChanged = PR_FALSE;
00336   
00337   if (aImage) {
00338     float p2t;
00339     p2t = GetPresContext()->PixelsToTwips();
00340 
00341     nsSize imageSizeInPx;
00342     aImage->GetWidth(&imageSizeInPx.width);
00343     aImage->GetHeight(&imageSizeInPx.height);
00344     nsSize newSize(NSIntPixelsToTwips(imageSizeInPx.width, p2t),
00345                    NSIntPixelsToTwips(imageSizeInPx.height, p2t));
00346     if (mIntrinsicSize != newSize) {
00347       intrinsicSizeChanged = PR_TRUE;
00348       mIntrinsicSize = newSize;
00349     }
00350   }
00351 
00352   // In any case, we need to translate this over appropriately.  Set
00353   // translation _before_ setting scaling so that it does not get
00354   // scaled!
00355 
00356   // XXXbz does this introduce rounding errors because of the cast to
00357   // float?  Should we just manually add that stuff in every time
00358   // instead?
00359   mTransform.SetToTranslate(float(mBorderPadding.left),
00360                             float(mBorderPadding.top - GetContinuationOffset()));
00361   
00362   // Set the scale factors
00363   if (mIntrinsicSize.width != 0 && mIntrinsicSize.height != 0 &&
00364       mIntrinsicSize != mComputedSize) {
00365     mTransform.AddScale(float(mComputedSize.width)  / float(mIntrinsicSize.width),
00366                         float(mComputedSize.height) / float(mIntrinsicSize.height));
00367   }
00368 
00369   return intrinsicSizeChanged;
00370 }
00371 
00372 /*
00373  * These two functions basically do the same check.  The first one
00374  * checks that the given request is the current request for our
00375  * mContent.  The second checks that the given image container the
00376  * same as the image container on the current request for our
00377  * mContent.
00378  */
00379 PRBool
00380 nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
00381 {
00382   // Default to pending load in case of errors
00383   nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
00384   NS_ASSERTION(imageLoader, "No image loading content?");
00385 
00386   PRInt32 requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
00387   imageLoader->GetRequestType(aRequest, &requestType);
00388 
00389   return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
00390 }
00391 
00392 PRBool
00393 nsImageFrame::IsPendingLoad(imgIContainer* aContainer) const
00394 {
00395   //  default to pending load in case of errors
00396   if (!aContainer) {
00397     NS_ERROR("No image container!");
00398     return PR_TRUE;
00399   }
00400 
00401   nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
00402   NS_ASSERTION(imageLoader, "No image loading content?");
00403   
00404   nsCOMPtr<imgIRequest> currentRequest;
00405   imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
00406                           getter_AddRefs(currentRequest));
00407   if (!currentRequest) {
00408     NS_ERROR("No current request");
00409     return PR_TRUE;
00410   }
00411 
00412   nsCOMPtr<imgIContainer> currentContainer;
00413   currentRequest->GetImage(getter_AddRefs(currentContainer));
00414 
00415   return currentContainer != aContainer;
00416   
00417 }
00418 
00419 nsRect
00420 nsImageFrame::SourceRectToDest(const nsRect& aRect)
00421 {
00422   float p2t = GetPresContext()->PixelsToTwips();
00423 
00424   // When scaling the image, row N of the source image may (depending on
00425   // the scaling function) be used to draw any row in the destination image
00426   // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
00427   // floating-point scaling factor.  The same holds true for columns.
00428   // So, we start by computing that bound without the floor and ceiling.
00429 
00430   nsRect r(NSIntPixelsToTwips(aRect.x - 1, p2t),
00431            NSIntPixelsToTwips(aRect.y - 1, p2t),
00432            NSIntPixelsToTwips(aRect.width + 2, p2t),
00433            NSIntPixelsToTwips(aRect.height + 2, p2t));
00434 
00435   mTransform.TransformCoord(&r.x, &r.y, &r.width, &r.height);
00436 
00437   // Now, round the edges out to the pixel boundary.
00438   int scale = (int) p2t;
00439   nscoord right = r.x + r.width;
00440   nscoord bottom = r.y + r.height;
00441 
00442   r.x -= (scale + (r.x % scale)) % scale;
00443   r.y -= (scale + (r.y % scale)) % scale;
00444   r.width = right + ((scale - (right % scale)) % scale) - r.x;
00445   r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
00446 
00447   return r;
00448 }
00449 
00450 nsresult
00451 nsImageFrame::HandleLoadError(PRInt16 aImageStatus)
00452 {
00453   if (!NS_CP_ACCEPTED(aImageStatus) &&
00454       aImageStatus == nsIContentPolicy::REJECT_SERVER) {
00455     // Don't display any alt feedback in this case; we're blocking images
00456     // from that site and don't care to see anything from them
00457     return NS_OK;
00458   }
00459   
00460   // If we have an image map, don't do anything here
00461   // XXXbz Why?  This is what the code used to do, but there's no good
00462   // reason for it....
00463 
00464   nsAutoString usemap;
00465   mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::usemap, usemap);    
00466   if (!usemap.IsEmpty()) {
00467     return NS_OK;
00468   }
00469 
00470   nsPresContext* presContext = GetPresContext();
00471   
00472   // Check if we want to use a placeholder box with an icon or just
00473   // let the the presShell make us into inline text.  Decide as follows:
00474   //
00475   //  - if our special "force icons" style is set, show an icon
00476   //  - else if our "do not show placeholders" pref is set, skip the icon
00477   //  - else:
00478   //  - if QuirksMode, and there is no alt attribute, and this is not an
00479   //    <object> (which could not possibly have such an attribute), show an
00480   //    icon.
00481   //  - if QuirksMode, and the IMG has a size, and the image is
00482   //    broken, not blocked, show an icon.
00483   //  - otherwise, skip the icon
00484 
00485   PRBool useSizedBox;
00486   
00487   const nsStyleUIReset* uiResetData = GetStyleUIReset();
00488   if (uiResetData->mForceBrokenImageIcon) {
00489     useSizedBox = PR_TRUE;
00490   }
00491   else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
00492     useSizedBox = PR_FALSE;
00493   }
00494   else {
00495     if (presContext->CompatibilityMode() != eCompatibility_NavQuirks) {
00496       useSizedBox = PR_FALSE;
00497     }
00498     else {
00499       // We are in quirks mode, so we can just check the tag name; no need to
00500       // check the namespace.
00501       nsINodeInfo *nodeInfo = mContent->GetNodeInfo();
00502 
00503       if (!mContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::alt) &&
00504           nodeInfo &&
00505           !nodeInfo->Equals(nsHTMLAtoms::object)) {
00506         useSizedBox = PR_TRUE;
00507       }
00508       else if (!NS_CP_ACCEPTED(aImageStatus)) {
00509         useSizedBox = PR_FALSE;
00510       }
00511       else {
00512         // check whether we have fixed size
00513         useSizedBox = HaveFixedSize(GetStylePosition());
00514       }
00515     }
00516   }
00517   
00518   if (!useSizedBox) {
00519     // let the presShell handle converting this into the inline alt
00520     // text frame
00521     nsIFrame* primaryFrame = nsnull;
00522     if (mContent->IsContentOfType(nsIContent::eHTML) &&
00523         (mContent->Tag() == nsHTMLAtoms::object ||
00524          mContent->Tag() == nsHTMLAtoms::embed)) {
00525       // We have to try to get the primary frame for mContent, since for
00526       // <object> the frame CantRenderReplacedElement wants is the
00527       // ObjectFrame, not us (we're an anonymous frame then)....
00528       presContext->PresShell()->GetPrimaryFrameFor(mContent, &primaryFrame);
00529     }
00530 
00531     if (!primaryFrame) {
00532       primaryFrame = this;
00533     }     
00534     
00535     presContext->PresShell()->CantRenderReplacedElement(primaryFrame);
00536     return NS_ERROR_FRAME_REPLACED;
00537   }
00538 
00539   // we are handling it
00540   // invalidate the icon area (it may change states)   
00541   InvalidateIcon();
00542   return NS_OK;
00543 }
00544 
00545 nsresult
00546 nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage)
00547 {
00548   if (!aImage) return NS_ERROR_INVALID_ARG;
00549 
00550   // handle iconLoads first...
00551   if (HandleIconLoads(aRequest, PR_FALSE)) {
00552     return NS_OK;
00553   }
00554 
00555   /* Get requested animation policy from the pres context:
00556    *   normal = 0
00557    *   one frame = 1
00558    *   one loop = 2
00559    */
00560   nsPresContext *presContext = GetPresContext();
00561   aImage->SetAnimationMode(presContext->ImageAnimationMode());
00562   // Ensure the animation (if any) is started.
00563   aImage->StartAnimation();
00564 
00565   if (IsPendingLoad(aRequest)) {
00566     // We don't care
00567     return NS_OK;
00568   }
00569   
00570   RecalculateTransform(aImage);
00571 
00572   // Now we need to reflow if we have an unconstrained size and have
00573   // already gotten the initial reflow
00574   if (!(mState & IMAGE_SIZECONSTRAINED) && (mState & IMAGE_GOTINITIALREFLOW)) { 
00575     nsIPresShell *presShell = presContext->GetPresShell();
00576     NS_ASSERTION(mParent, "No parent to pass the reflow request up to.");
00577     NS_ASSERTION(presShell, "No PresShell.");
00578     if (mParent && presShell) { 
00579       mState |= NS_FRAME_IS_DIRTY;
00580       mParent->ReflowDirtyChild(presShell, NS_STATIC_CAST(nsIFrame*, this));
00581     }
00582   }
00583 
00584   return NS_OK;
00585 }
00586 
00587 nsresult
00588 nsImageFrame::OnDataAvailable(imgIRequest *aRequest,
00589                               gfxIImageFrame *aFrame,
00590                               const nsRect *aRect)
00591 {
00592   // XXX do we need to make sure that the reflow from the
00593   // OnStartContainer has been processed before we start calling
00594   // invalidate?
00595 
00596   NS_ENSURE_ARG_POINTER(aRect);
00597 
00598   if (!(mState & IMAGE_GOTINITIALREFLOW)) {
00599     // Don't bother to do anything; we have a reflow coming up!
00600     return NS_OK;
00601   }
00602   
00603   // handle iconLoads first...
00604   if (HandleIconLoads(aRequest, PR_FALSE)) {
00605     // Image changed, invalidate
00606     Invalidate(*aRect, PR_FALSE);
00607     return NS_OK;
00608   }
00609 
00610   if (IsPendingLoad(aRequest)) {
00611     // We don't care
00612     return NS_OK;
00613   }
00614 
00615   // Don't invalidate if the current visible frame isn't the one the data is
00616   // from
00617   nsCOMPtr<imgIContainer> container;
00618   aRequest->GetImage(getter_AddRefs(container));
00619   if (container) {
00620     nsCOMPtr<gfxIImageFrame> currentFrame;
00621     container->GetCurrentFrame(getter_AddRefs(currentFrame));
00622     if (aFrame != currentFrame) {
00623       // just bail
00624       return NS_OK;
00625     }
00626   }
00627 
00628   nsRect r = SourceRectToDest(*aRect);
00629 #ifdef DEBUG_decode
00630   printf("Source rect (%d,%d,%d,%d) -> invalidate dest rect (%d,%d,%d,%d)\n",
00631          aRect->x, aRect->y, aRect->width, aRect->height,
00632          r.x, r.y, r.width, r.height);
00633 #endif
00634 
00635   Invalidate(r, PR_FALSE);
00636   
00637   return NS_OK;
00638 }
00639 
00640 nsresult
00641 nsImageFrame::OnStopDecode(imgIRequest *aRequest,
00642                            nsresult aStatus,
00643                            const PRUnichar *aStatusArg)
00644 {
00645   nsPresContext *presContext = GetPresContext();
00646   nsIPresShell *presShell = presContext->GetPresShell();
00647   NS_ASSERTION(presShell, "No PresShell.");
00648 
00649   // handle iconLoads first...
00650   if (HandleIconLoads(aRequest, NS_SUCCEEDED(aStatus))) {
00651     return NS_OK;
00652   }
00653 
00654   // Check what request type we're dealing with
00655   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
00656   NS_ASSERTION(imageLoader, "Who's notifying us??");
00657   PRInt32 loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
00658   imageLoader->GetRequestType(aRequest, &loadType);
00659   if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
00660       loadType != nsIImageLoadingContent::PENDING_REQUEST) {
00661     return NS_ERROR_FAILURE;
00662   }
00663 
00664   if (loadType == nsIImageLoadingContent::PENDING_REQUEST) {
00665     // May have to switch sizes here!
00666     PRBool intrinsicSizeChanged = PR_TRUE;
00667     if (NS_SUCCEEDED(aStatus)) {
00668       nsCOMPtr<imgIContainer> imageContainer;
00669       aRequest->GetImage(getter_AddRefs(imageContainer));
00670       NS_ASSERTION(imageContainer, "Successful load with no container?");
00671       intrinsicSizeChanged = RecalculateTransform(imageContainer);
00672     }
00673     else {
00674       // Have to size to 0,0 so that GetDesiredSize recalculates the size
00675       mIntrinsicSize.SizeTo(0, 0);
00676     }
00677 
00678     if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we havn't gotten the inital reflow yet
00679       if (!(mState & IMAGE_SIZECONSTRAINED) && intrinsicSizeChanged) {
00680         NS_ASSERTION(mParent, "No parent to pass the reflow request up to.");
00681         if (mParent && presShell) { 
00682           mState |= NS_FRAME_IS_DIRTY;
00683           mParent->ReflowDirtyChild(presShell, NS_STATIC_CAST(nsIFrame*, this));
00684         }
00685       } else {
00686         nsSize s = GetSize();
00687         nsRect r(0, 0, s.width, s.height);
00688         // Update border+content to account for image change
00689         Invalidate(r, PR_FALSE);
00690       }
00691     }
00692   }
00693 
00694   // if src failed to load, determine how to handle it: 
00695   //  - either render the ALT text in this frame, or let the presShell
00696   //  handle it
00697   if (NS_FAILED(aStatus) && aStatus != NS_ERROR_IMAGE_SRC_CHANGED) {
00698     PRInt16 imageStatus = nsIContentPolicy::ACCEPT;
00699     imageLoader->GetImageBlockingStatus(&imageStatus);
00700     HandleLoadError(imageStatus);
00701   }
00702 
00703   return NS_OK;
00704 }
00705 
00706 nsresult
00707 nsImageFrame::FrameChanged(imgIContainer *aContainer,
00708                            gfxIImageFrame *aNewFrame,
00709                            nsRect *aDirtyRect)
00710 {
00711   if (!GetStyleVisibility()->IsVisible()) {
00712     return NS_OK;
00713   }
00714 
00715   if (IsPendingLoad(aContainer)) {
00716     // We don't care about it
00717     return NS_OK;
00718   }
00719   
00720   nsRect r = SourceRectToDest(*aDirtyRect);
00721 
00722   // Update border+content to account for image change
00723   Invalidate(r, PR_FALSE);
00724   return NS_OK;
00725 }
00726 
00727 
00728 #define MINMAX(_value,_min,_max) \
00729     ((_value) < (_min)           \
00730      ? (_min)                    \
00731      : ((_value) > (_max)        \
00732         ? (_max)                 \
00733         : (_value)))
00734 
00735 void
00736 nsImageFrame::GetDesiredSize(nsPresContext* aPresContext,
00737                              const nsHTMLReflowState& aReflowState,
00738                              nsHTMLReflowMetrics& aDesiredSize)
00739 {
00740   // if mIntrinsicSize.width and height are 0, then we should
00741   // check to see if the size is already known by the image container.
00742   if (mIntrinsicSize.width == 0 && mIntrinsicSize.height == 0) {
00743     nsCOMPtr<imgIRequest> currentRequest;
00744     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
00745     if (imageLoader) {
00746       imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
00747                               getter_AddRefs(currentRequest));
00748     }
00749     nsCOMPtr<imgIContainer> currentContainer;
00750     if (currentRequest) {
00751       currentRequest->GetImage(getter_AddRefs(currentContainer));
00752     }
00753       
00754     float p2t;
00755     p2t = aPresContext->PixelsToTwips();
00756 
00757     if (currentContainer) {
00758       RecalculateTransform(currentContainer);
00759     } else {
00760       // image request is null or image size not known, probably an
00761       // invalid image specified
00762       // - make the image big enough for the icon (it may not be
00763       // used if inline alt expansion is used instead)
00764       // XXX: we need this in composer, but it is also good for
00765       // XXX: general quirks mode to always have room for the icon
00766       if (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
00767         mIntrinsicSize.SizeTo(NSIntPixelsToTwips(ICON_SIZE+(2*(ICON_PADDING+ALT_BORDER_WIDTH)), p2t),
00768                               NSIntPixelsToTwips(ICON_SIZE+(2*(ICON_PADDING+ALT_BORDER_WIDTH)), p2t));
00769       }
00770       RecalculateTransform(nsnull);
00771     }
00772   }
00773 
00774   // Handle intrinsic sizes and their interaction with
00775   // {min-,max-,}{width,height} according to the rules in
00776   // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
00777 
00778   // Note: throughout the following section of the function, I avoid
00779   // a * (b / c) because of its reduced accuracy relative to a * b / c
00780   // or (a * b) / c (which are equivalent).
00781 
00782   // convert from normal twips to scaled twips (printing...)
00783   float t2st = aPresContext->TwipsToPixels() *
00784     aPresContext->ScaledPixelsToTwips(); // twips to scaled twips
00785   nscoord intrinsicWidth =
00786       NSToCoordRound(float(mIntrinsicSize.width) * t2st);
00787   nscoord intrinsicHeight =
00788       NSToCoordRound(float(mIntrinsicSize.height) * t2st);
00789 
00790   // Determine whether the image has fixed content width
00791   nscoord width = aReflowState.mComputedWidth;
00792   nscoord minWidth = aReflowState.mComputedMinWidth;
00793   nscoord maxWidth = aReflowState.mComputedMaxWidth;
00794 
00795   // Determine whether the image has fixed content height
00796   nscoord height = aReflowState.mComputedHeight;
00797   nscoord minHeight = aReflowState.mComputedMinHeight;
00798   nscoord maxHeight = aReflowState.mComputedMaxHeight;
00799 
00800   PRBool isAutoWidth = width == NS_INTRINSICSIZE;
00801   PRBool isAutoHeight = height == NS_UNCONSTRAINEDSIZE;
00802   if (isAutoWidth) {
00803     if (isAutoHeight) {
00804 
00805       // 'auto' width, 'auto' height
00806       // XXX nsHTMLReflowState should already ensure this
00807       if (minWidth > maxWidth)
00808         maxWidth = minWidth;
00809       if (minHeight > maxHeight)
00810         maxHeight = minHeight;
00811 
00812       nscoord heightAtMaxWidth, heightAtMinWidth,
00813               widthAtMaxHeight, widthAtMinHeight;
00814       if (intrinsicWidth > 0) {
00815         heightAtMaxWidth = maxWidth * intrinsicHeight / intrinsicWidth;
00816         if (heightAtMaxWidth < minHeight)
00817           heightAtMaxWidth = minHeight;
00818         heightAtMinWidth = minWidth * intrinsicHeight / intrinsicWidth;
00819         if (heightAtMinWidth > maxHeight)
00820           heightAtMinWidth = maxHeight;
00821       } else {
00822         heightAtMaxWidth = intrinsicHeight;
00823         heightAtMinWidth = intrinsicHeight;
00824       }
00825 
00826       if (intrinsicHeight > 0) {
00827         widthAtMaxHeight = maxHeight * intrinsicWidth / intrinsicHeight;
00828         if (widthAtMaxHeight < minWidth)
00829           widthAtMaxHeight = minWidth;
00830         widthAtMinHeight = minHeight * intrinsicWidth / intrinsicHeight;
00831         if (widthAtMinHeight > maxWidth)
00832           widthAtMinHeight = maxWidth;
00833       } else {
00834         widthAtMaxHeight = intrinsicWidth;
00835         widthAtMinHeight = intrinsicWidth;
00836       }
00837 
00838       if (intrinsicWidth > maxWidth) {
00839         if (intrinsicHeight > maxHeight) {
00840           if (maxWidth * intrinsicHeight <= maxHeight * intrinsicWidth) {
00841             width = maxWidth;
00842             height = heightAtMaxWidth;
00843           } else {
00844             height = maxHeight;
00845             width = widthAtMaxHeight;
00846           }
00847         } else {
00848           width = maxWidth;
00849           height = heightAtMaxWidth;
00850         }
00851       } else if (intrinsicWidth < minWidth) {
00852         if (intrinsicHeight < minHeight) {
00853           if (minWidth * intrinsicHeight <= minHeight * intrinsicWidth) {
00854             height = minHeight;
00855             width = widthAtMinHeight;
00856           } else {
00857             width = minWidth;
00858             height = heightAtMinWidth;
00859           }
00860         } else {
00861           width = minWidth;
00862           height = heightAtMinWidth;
00863         }
00864       } else {
00865         if (intrinsicHeight > maxHeight) {
00866           height = maxHeight;
00867           width = widthAtMaxHeight;
00868         } else if (intrinsicHeight < minHeight) {
00869           height = minHeight;
00870           width = widthAtMinHeight;
00871         } else {
00872           width = intrinsicWidth;
00873           height = intrinsicHeight;
00874         }
00875       }
00876 
00877     } else {
00878 
00879       // 'auto' width, non-'auto' height
00880       // XXX nsHTMLReflowState should already ensure this
00881       height = MINMAX(height, minHeight, maxHeight);
00882       if (intrinsicHeight != 0) {
00883         width = intrinsicWidth * height / intrinsicHeight;
00884       } else {
00885         width = intrinsicWidth;
00886       }
00887       width = MINMAX(width, minWidth, maxWidth);
00888 
00889     }
00890   } else {
00891     if (isAutoHeight) {
00892 
00893       // non-'auto' width, 'auto' height
00894       // XXX nsHTMLReflowState should already ensure this
00895       width = MINMAX(width, minWidth, maxWidth);
00896       if (intrinsicWidth != 0) {
00897         height = intrinsicHeight * width / intrinsicWidth;
00898       } else {
00899         height = intrinsicHeight;
00900       }
00901       height = MINMAX(height, minHeight, maxHeight);
00902 
00903     } else {
00904 
00905       // non-'auto' width, non-'auto' height
00906       // XXX nsHTMLReflowState should already ensure this
00907       height = MINMAX(height, minHeight, maxHeight);
00908       // XXX nsHTMLReflowState should already ensure this
00909       width = MINMAX(width, minWidth, maxWidth);
00910 
00911     }
00912   }
00913 
00914   if (mComputedSize.width != width || mComputedSize.height != height) {
00915     mComputedSize.SizeTo(width, height);
00916     RecalculateTransform(nsnull);
00917   }
00918 
00919   aDesiredSize.width = mComputedSize.width;
00920   aDesiredSize.height = mComputedSize.height;
00921 }
00922 
00923 nsRect 
00924 nsImageFrame::GetInnerArea() const
00925 {
00926   nsRect r;
00927   r.x = mBorderPadding.left;
00928   r.y = mPrevInFlow ? 0 : mBorderPadding.top;
00929   r.width = mRect.width - mBorderPadding.left - mBorderPadding.right;
00930   r.height = mRect.height -
00931     (mPrevInFlow ? 0 : mBorderPadding.top) -
00932     (mNextInFlow ? 0 : mBorderPadding.bottom);
00933   return r;
00934 }
00935 
00936 // get the offset into the content area of the image where aImg starts if it is a continuation.
00937 nscoord 
00938 nsImageFrame::GetContinuationOffset(nscoord* aWidth) const
00939 {
00940   nscoord offset = 0;
00941   if (aWidth) {
00942     *aWidth = 0;
00943   }
00944 
00945   if (mPrevInFlow) {
00946     for (nsIFrame* prevInFlow = mPrevInFlow ; prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) {
00947       nsRect rect = prevInFlow->GetRect();
00948       if (aWidth) {
00949         *aWidth = rect.width;
00950       }
00951       offset += rect.height;
00952     }
00953     offset -= mBorderPadding.top;
00954     offset = PR_MAX(0, offset);
00955   }
00956   return offset;
00957 }
00958 
00959 NS_IMETHODIMP
00960 nsImageFrame::Reflow(nsPresContext*          aPresContext,
00961                      nsHTMLReflowMetrics&     aMetrics,
00962                      const nsHTMLReflowState& aReflowState,
00963                      nsReflowStatus&          aStatus)
00964 {
00965   DO_GLOBAL_REFLOW_COUNT("nsImageFrame", aReflowState.reason);
00966   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
00967   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
00968                   ("enter nsImageFrame::Reflow: availSize=%d,%d",
00969                   aReflowState.availableWidth, aReflowState.availableHeight));
00970 
00971   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
00972 
00973   aStatus = NS_FRAME_COMPLETE;
00974 
00975   // see if we have a frozen size (i.e. a fixed width and height)
00976   if (HaveFixedSize(aReflowState)) {
00977     mState |= IMAGE_SIZECONSTRAINED;
00978   } else {
00979     mState &= ~IMAGE_SIZECONSTRAINED;
00980   }
00981 
00982   if (aReflowState.reason == eReflowReason_Initial) {
00983     mState |= IMAGE_GOTINITIALREFLOW;
00984   }
00985 
00986   // Set our borderpadding so that if GetDesiredSize has to recalc the
00987   // transform it can.
00988   mBorderPadding   = aReflowState.mComputedBorderPadding;
00989 
00990   // get the desired size of the complete image
00991   GetDesiredSize(aPresContext, aReflowState, aMetrics);
00992 
00993   // add borders and padding
00994   aMetrics.width  += mBorderPadding.left + mBorderPadding.right;
00995   aMetrics.height += mBorderPadding.top + mBorderPadding.bottom;
00996   
00997   if (mPrevInFlow) {
00998     nscoord y = GetContinuationOffset(&aMetrics.width);
00999     aMetrics.height -= y + mBorderPadding.top;
01000     aMetrics.height = PR_MAX(0, aMetrics.height);
01001   }
01002 
01003 
01004   // we have to split images if we are:
01005   //  in Paginated mode, we need to have a constrained height, and have a height larger than our available height
01006   PRUint32 loadStatus = imgIRequest::STATUS_NONE;
01007   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
01008   NS_ASSERTION(imageLoader, "No content node??");
01009   if (imageLoader) {
01010     nsCOMPtr<imgIRequest> currentRequest;
01011     imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
01012                             getter_AddRefs(currentRequest));
01013     if (currentRequest) {
01014       currentRequest->GetImageStatus(&loadStatus);
01015     }
01016   }
01017   if (aPresContext->IsPaginated() &&
01018       ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
01019       NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight && 
01020       aMetrics.height > aReflowState.availableHeight) { 
01021     // split an image frame but not an image control frame
01022     if (nsLayoutAtoms::imageFrame == GetType()) {
01023       // our desired height was greater than 0, so to avoid infinite splitting, use 1 pixel as the min
01024       aMetrics.height = PR_MAX(NSToCoordRound(aPresContext->ScaledPixelsToTwips()), aReflowState.availableHeight);
01025       aStatus = NS_FRAME_NOT_COMPLETE;
01026     }
01027   }
01028   aMetrics.ascent  = aMetrics.height;
01029   aMetrics.descent = 0;
01030 
01031   if (aMetrics.mComputeMEW) {
01032     aMetrics.SetMEWToActualWidth(aReflowState.mStylePosition->mWidth.GetUnit());
01033   }
01034   
01035   if (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH) {
01036     aMetrics.mMaximumWidth = aMetrics.width;
01037   }
01038   aMetrics.mOverflowArea.SetRect(0, 0, aMetrics.width, aMetrics.height);
01039   FinishAndStoreOverflow(&aMetrics);
01040 
01041   // Now that that's all done, check whether we're resizing... if we are,
01042   // invalidate our rect.
01043   // XXXbz we really only want to do this when reflow is completely done, but
01044   // we have no way to detect when mRect changes (since SetRect is non-virtual,
01045   // so this is the best we can do).
01046   if (mRect.width != aMetrics.width || mRect.height != aMetrics.height) {
01047     Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_FALSE);
01048   }
01049 
01050   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
01051                   ("exit nsImageFrame::Reflow: size=%d,%d",
01052                   aMetrics.width, aMetrics.height));
01053   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
01054   return NS_OK;
01055 }
01056 
01057 // Computes the width of the specified string. aMaxWidth specifies the maximum
01058 // width available. Once this limit is reached no more characters are measured.
01059 // The number of characters that fit within the maximum width are returned in
01060 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
01061 // into the rendering context before this is called (for performance). MMP
01062 void
01063 nsImageFrame::MeasureString(const PRUnichar*     aString,
01064                             PRInt32              aLength,
01065                             nscoord              aMaxWidth,
01066                             PRUint32&            aMaxFit,
01067                             nsIRenderingContext& aContext)
01068 {
01069   nscoord totalWidth = 0;
01070   nscoord spaceWidth;
01071   aContext.GetWidth(' ', spaceWidth);
01072 
01073   aMaxFit = 0;
01074   while (aLength > 0) {
01075     // Find the next place we can line break
01076     PRUint32  len = aLength;
01077     PRBool    trailingSpace = PR_FALSE;
01078     for (PRInt32 i = 0; i < aLength; i++) {
01079       if (XP_IS_SPACE(aString[i]) && (i > 0)) {
01080         len = i;  // don't include the space when measuring
01081         trailingSpace = PR_TRUE;
01082         break;
01083       }
01084     }
01085   
01086     // Measure this chunk of text, and see if it fits
01087     nscoord width;
01088     aContext.GetWidth(aString, len, width);
01089     PRBool  fits = (totalWidth + width) <= aMaxWidth;
01090 
01091     // If it fits on the line, or it's the first word we've processed then
01092     // include it
01093     if (fits || (0 == totalWidth)) {
01094       // New piece fits
01095       totalWidth += width;
01096 
01097       // If there's a trailing space then see if it fits as well
01098       if (trailingSpace) {
01099         if ((totalWidth + spaceWidth) <= aMaxWidth) {
01100           totalWidth += spaceWidth;
01101         } else {
01102           // Space won't fit. Leave it at the end but don't include it in
01103           // the width
01104           fits = PR_FALSE;
01105         }
01106 
01107         len++;
01108       }
01109 
01110       aMaxFit += len;
01111       aString += len;
01112       aLength -= len;
01113     }
01114 
01115     if (!fits) {
01116       break;
01117     }
01118   }
01119 }
01120 
01121 // Formats the alt-text to fit within the specified rectangle. Breaks lines
01122 // between words if a word would extend past the edge of the rectangle
01123 void
01124 nsImageFrame::DisplayAltText(nsPresContext*      aPresContext,
01125                              nsIRenderingContext& aRenderingContext,
01126                              const nsString&      aAltText,
01127                              const nsRect&        aRect)
01128 {
01129   // Set font and color
01130   aRenderingContext.SetColor(GetStyleColor()->mColor);
01131   SetFontFromStyle(&aRenderingContext, mStyleContext);
01132 
01133   // Format the text to display within the formatting rect
01134   nsIFontMetrics* fm;
01135   aRenderingContext.GetFontMetrics(fm);
01136 
01137   nscoord maxAscent, maxDescent, height;
01138   fm->GetMaxAscent(maxAscent);
01139   fm->GetMaxDescent(maxDescent);
01140   fm->GetHeight(height);
01141 
01142   // XXX It would be nice if there was a way to have the font metrics tell
01143   // use where to break the text given a maximum width. At a minimum we need
01144   // to be able to get the break character...
01145   const PRUnichar* str = aAltText.get();
01146   PRInt32          strLen = aAltText.Length();
01147   nscoord          y = aRect.y;
01148   // Always show the first line, even if we have to clip it below
01149   PRBool firstLine = PR_TRUE;
01150   while ((strLen > 0) && (firstLine || (y + maxDescent) < aRect.YMost())) {
01151     // Determine how much of the text to display on this line
01152     PRUint32  maxFit;  // number of characters that fit
01153     MeasureString(str, strLen, aRect.width, maxFit, aRenderingContext);
01154     
01155     // Display the text
01156     aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent);
01157 
01158     // Move to the next line
01159     str += maxFit;
01160     strLen -= maxFit;
01161     y += height;
01162     firstLine = PR_FALSE;
01163   }
01164 
01165   NS_RELEASE(fm);
01166 }
01167 
01168 struct nsRecessedBorder : public nsStyleBorder {
01169   nsRecessedBorder(nscoord aBorderWidth)
01170     : nsStyleBorder()
01171   {
01172     NS_FOR_CSS_SIDES(side) {
01173       // Note: use SetBorderColor here because we want to make sure
01174       // the "special" flags are unset.
01175       SetBorderColor(side, NS_RGB(0, 0, 0));
01176       mBorder.side(side) = aBorderWidth;
01177       // Note: use SetBorderStyle here because we want to affect
01178       // mComputedBorder
01179       SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
01180     }
01181   }
01182 };
01183 
01184 void
01185 nsImageFrame::DisplayAltFeedback(nsPresContext*      aPresContext,
01186                                  nsIRenderingContext& aRenderingContext,
01187                                  imgIRequest*         aRequest)
01188 {
01189   // Calculate the inner area
01190   nsRect  inner = GetInnerArea();
01191 
01192   // Display a recessed one pixel border
01193   nscoord borderEdgeWidth;
01194   float   p2t = aPresContext->ScaledPixelsToTwips();
01195   borderEdgeWidth = NSIntPixelsToTwips(ALT_BORDER_WIDTH, p2t);
01196 
01197   // if inner area is empty, then make it big enough for at least the icon
01198   if (inner.IsEmpty()){
01199     inner.SizeTo(2*(NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH,p2t)),
01200                  2*(NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH,p2t)));
01201   }
01202 
01203   // Make sure we have enough room to actually render the border within
01204   // our frame bounds
01205   if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
01206     return;
01207   }
01208 
01209   // Paint the border
01210   nsRecessedBorder recessedBorder(borderEdgeWidth);
01211   nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, inner,
01212                               inner, recessedBorder, mStyleContext, 0);
01213 
01214   // Adjust the inner rect to account for the one pixel recessed border,
01215   // and a six pixel padding on each edge
01216   inner.Deflate(NSIntPixelsToTwips(ICON_PADDING+ALT_BORDER_WIDTH, p2t), 
01217                 NSIntPixelsToTwips(ICON_PADDING+ALT_BORDER_WIDTH, p2t));
01218   if (inner.IsEmpty()) {
01219     return;
01220   }
01221 
01222   // Clip so we don't render outside the inner rect
01223   aRenderingContext.PushState();
01224   aRenderingContext.SetClipRect(inner, nsClipCombine_kIntersect);
01225 
01226   PRBool dispIcon = gIconLoad ? gIconLoad->mPrefShowPlaceholders : PR_TRUE;
01227 
01228   // Check if we should display image placeholders
01229   if (dispIcon) {
01230     PRInt32 size = NSIntPixelsToTwips(ICON_SIZE, p2t);
01231 
01232     PRBool iconUsed = PR_FALSE;
01233 
01234     // see if the icon images are present...
01235     if (gIconLoad && gIconLoad->mIconsLoaded) {
01236       // pick the correct image
01237       nsCOMPtr<imgIContainer> imgCon;
01238       if (aRequest) {
01239         aRequest->GetImage(getter_AddRefs(imgCon));
01240       }
01241       if (imgCon) {
01242         // draw it
01243         nsRect source(0,0,size,size);
01244         nsRect dest(inner.x,inner.y,size,size);
01245         aRenderingContext.DrawImage(imgCon, source, dest);
01246         iconUsed = PR_TRUE;
01247       }
01248     }
01249 
01250     // if we could not draw the image, then just draw some grafitti
01251     if (!iconUsed) {
01252       nscolor oldColor;
01253       aRenderingContext.DrawRect(0,0,size,size);
01254       aRenderingContext.GetColor(oldColor);
01255       aRenderingContext.SetColor(NS_RGB(0xFF,0,0));
01256       aRenderingContext.FillEllipse(NS_STATIC_CAST(int,size/2),NS_STATIC_CAST(int,size/2),
01257                                     NS_STATIC_CAST(int,(size/2)-(2*p2t)),NS_STATIC_CAST(int,(size/2)-(2*p2t)));
01258       aRenderingContext.SetColor(oldColor);
01259     }  
01260 
01261     // Reduce the inner rect by the width of the icon, and leave an
01262     // additional ICON_PADDING pixels for padding
01263     PRInt32 iconWidth = NSIntPixelsToTwips(ICON_SIZE + ICON_PADDING, p2t);
01264     inner.x += iconWidth;
01265     inner.width -= iconWidth;
01266   }
01267 
01268   // If there's still room, display the alt-text
01269   if (!inner.IsEmpty()) {
01270     nsIContent* content = GetContent();
01271     if (content) {
01272       nsXPIDLString altText;
01273       nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(),
01274                                                  altText);
01275       DisplayAltText(aPresContext, aRenderingContext, altText, inner);
01276     }
01277   }
01278 
01279   aRenderingContext.PopState();
01280 }
01281 
01282 NS_METHOD
01283 nsImageFrame::Paint(nsPresContext*      aPresContext,
01284                     nsIRenderingContext& aRenderingContext,
01285                     const nsRect&        aDirtyRect,
01286                     nsFramePaintLayer    aWhichLayer,
01287                     PRUint32             aFlags)
01288 {
01289   PRBool isVisible;
01290   if (NS_SUCCEEDED(IsVisibleForPainting(aPresContext, aRenderingContext, PR_TRUE, &isVisible)) && 
01291       isVisible && mRect.width && mRect.height) {
01292     // If painting is suppressed, we need to stop image painting.  We
01293     // have to cover <img> here because of input image controls.
01294     PRBool paintingSuppressed = PR_FALSE;
01295     aPresContext->PresShell()->IsPaintingSuppressed(&paintingSuppressed);
01296     if (paintingSuppressed) {
01297       return NS_OK;
01298     }
01299 
01300     // First paint background and borders, which should be in the
01301     // FOREGROUND or BACKGROUND paint layer if the element is
01302     // inline-level or block-level, respectively (bug 36710).  (See
01303     // CSS2 9.5, which is the rationale for paint layers.)
01304     const nsStyleDisplay* display = GetStyleDisplay();
01305     nsFramePaintLayer backgroundLayer = display->IsBlockLevel()
01306                                             ? NS_FRAME_PAINT_LAYER_BACKGROUND
01307                                             : NS_FRAME_PAINT_LAYER_FOREGROUND;
01308     if (aWhichLayer == backgroundLayer) {
01309       PaintSelf(aPresContext, aRenderingContext, aDirtyRect);
01310     }
01311 
01312     if (mComputedSize.width != 0 && mComputedSize.height != 0) {
01313       nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
01314       NS_ASSERTION(mContent, "Not an image loading content?");
01315 
01316       nsCOMPtr<imgIRequest> currentRequest;
01317       if (imageLoader) {
01318         imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
01319                                 getter_AddRefs(currentRequest));
01320       }
01321       
01322       nsCOMPtr<imgIContainer> imgCon;
01323 
01324       PRUint32 loadStatus = imgIRequest::STATUS_ERROR;
01325 
01326       if (currentRequest) {
01327         currentRequest->GetImage(getter_AddRefs(imgCon));
01328         currentRequest->GetImageStatus(&loadStatus);
01329       }
01330 
01331       if (loadStatus & imgIRequest::STATUS_ERROR || !imgCon) {
01332         // No image yet, or image load failed. Draw the alt-text and an icon
01333         // indicating the status (unless image is blocked, in which case we show nothing)
01334 
01335         PRInt16 imageStatus = nsIContentPolicy::ACCEPT;
01336         if (imageLoader) {
01337           imageLoader->GetImageBlockingStatus(&imageStatus);
01338         }
01339       
01340         if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer &&
01341             (NS_CP_ACCEPTED(imageStatus) ||
01342              imageStatus != nsIContentPolicy::REJECT_SERVER)) {
01343           DisplayAltFeedback(aPresContext, aRenderingContext, 
01344                              (loadStatus & imgIRequest::STATUS_ERROR)
01345                              ? gIconLoad->mBrokenImage
01346                              : gIconLoad->mLoadingImage);
01347         }
01348       }
01349       else {
01350         if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer && imgCon) {
01351           // Render the image into our content area (the area inside
01352           // the borders and padding)
01353           nsRect inner = GetInnerArea();
01354           nsRect paintArea(inner);
01355 
01356           nscoord offsetY = 0; 
01357 
01358           // if the image is split account for y-offset
01359           if (mPrevInFlow) {
01360             offsetY = GetContinuationOffset();
01361           }
01362 
01363           if (mIntrinsicSize == mComputedSize) {
01364             // Find the actual rect to be painted to in the rendering context
01365             paintArea.IntersectRect(paintArea, aDirtyRect);
01366 
01367             // Rect in the image to paint
01368             nsRect r(paintArea.x - inner.x,
01369                      paintArea.y - inner.y + offsetY,
01370                      paintArea.width,
01371                      paintArea.height);
01372           
01373             aRenderingContext.DrawImage(imgCon, r, paintArea);
01374           } else {
01375             // The computed size is the total size of all the continuations,
01376             // including ourselves.  Note that we're basically inverting
01377             // mTransform here (would it too much to ask for
01378             // nsTransform2D::Invert?), since we need to convert from
01379             // rendering context coords to image coords...
01380             nsTransform2D trans;
01381             trans.SetToScale((float(mIntrinsicSize.width) / float(mComputedSize.width)),
01382                              (float(mIntrinsicSize.height) / float(mComputedSize.height)));
01383           
01384             // XXXbz it looks like we should take
01385             // IntersectRect(paintArea, aDirtyRect) here too, but things
01386             // get very weird if I do that ....
01387             //   paintArea.IntersectRect(paintArea, aDirtyRect);
01388           
01389             // dirty rect in image our coord size...
01390             nsRect r(paintArea.x - inner.x,
01391                      paintArea.y - inner.y + offsetY,
01392                      paintArea.width,
01393                      paintArea.height);
01394 
01395             // Transform that to image coords
01396             trans.TransformCoord(&r.x, &r.y, &r.width, &r.height);
01397           
01398 #ifdef DEBUG_decode
01399             printf("IF draw src (%d,%d,%d,%d) -> dst (%d,%d,%d,%d)\n",
01400                    r.x, r.y, r.width, r.height, paintArea.x, paintArea.y,
01401                    paintArea.width, paintArea.height);
01402 #endif
01403 
01404             aRenderingContext.DrawImage(imgCon, r, paintArea);
01405           }
01406         }
01407 
01408         nsImageMap* map = GetImageMap(aPresContext);
01409         if (nsnull != map) {
01410           nsRect inner = GetInnerArea();
01411           aRenderingContext.PushState();
01412           aRenderingContext.SetColor(NS_RGB(0, 0, 0));
01413           aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
01414           aRenderingContext.Translate(inner.x, inner.y);
01415           map->Draw(aPresContext, aRenderingContext);
01416           aRenderingContext.PopState();
01417         }
01418 
01419 #ifdef DEBUG
01420         if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) &&
01421             GetShowFrameBorders()) {
01422           nsImageMap* map = GetImageMap(aPresContext);
01423           if (nsnull != map) {
01424             nsRect inner = GetInnerArea();
01425             aRenderingContext.SetColor(NS_RGB(0, 0, 0));
01426             aRenderingContext.PushState();
01427             aRenderingContext.Translate(inner.x, inner.y);
01428             map->Draw(aPresContext, aRenderingContext);
01429             aRenderingContext.PopState();
01430           }
01431         }
01432 #endif
01433       }
01434     }
01435   }
01436   PRInt16 displaySelection = 0;
01437 
01438   nsresult result; 
01439   result = aPresContext->PresShell()->GetSelectionFlags(&displaySelection);
01440   if (NS_FAILED(result))
01441     return result;
01442   if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
01443     return NS_OK;//no need to check the blue border, we cannot be drawn selected
01444 //insert hook here for image selection drawing
01445 #if IMAGE_EDITOR_CHECK
01446   //check to see if this frame is in an editor context
01447   //isEditor check. this needs to be changed to have better way to check
01448   if (displaySelection == nsISelectionDisplay::DISPLAY_ALL) 
01449   {
01450     nsCOMPtr<nsISelectionController> selCon;
01451     result = GetSelectionController(aPresContext, getter_AddRefs(selCon));
01452     if (NS_SUCCEEDED(result) && selCon)
01453     {
01454       nsCOMPtr<nsISelection> selection;
01455       result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
01456       if (NS_SUCCEEDED(result) && selection)
01457       {
01458         PRInt32 rangeCount;
01459         selection->GetRangeCount(&rangeCount);
01460         if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
01461         {
01462           nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
01463           if (parentContent)
01464           {
01465             PRInt32 thisOffset = parentContent->IndexOf(mContent);
01466             nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent);
01467             nsCOMPtr<nsIDOMNode> rangeNode;
01468             PRInt32 rangeOffset;
01469             nsCOMPtr<nsIDOMRange> range;
01470             selection->GetRangeAt(0,getter_AddRefs(range));
01471             if (range)
01472             {
01473               range->GetStartContainer(getter_AddRefs(rangeNode));
01474               range->GetStartOffset(&rangeOffset);
01475 
01476               if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset)
01477               {
01478                 range->GetEndContainer(getter_AddRefs(rangeNode));
01479                 range->GetEndOffset(&rangeOffset);
01480                 if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only
01481                   return NS_OK; //do not allow nsFrame do draw any further selection
01482               }
01483             }
01484           }
01485         }
01486       }
01487     }
01488   }
01489 #endif
01490   
01491   return nsFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer, nsISelectionDisplay::DISPLAY_IMAGES);
01492 }
01493 
01494 NS_IMETHODIMP
01495 nsImageFrame::GetImageMap(nsPresContext *aPresContext, nsIImageMap **aImageMap)
01496 {
01497   nsImageMap *map = GetImageMap(aPresContext);
01498   return CallQueryInterface(map, aImageMap);
01499 }
01500 
01501 nsImageMap*
01502 nsImageFrame::GetImageMap(nsPresContext* aPresContext)
01503 {
01504   if (!mImageMap) {
01505     nsIDocument* doc = mContent->GetDocument();
01506     if (!doc) {
01507       return nsnull;
01508     }
01509 
01510     nsAutoString usemap;
01511     mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::usemap, usemap);
01512 
01513     nsCOMPtr<nsIDOMHTMLMapElement> map = nsImageMapUtils::FindImageMap(doc,usemap);
01514     if (map) {
01515       mImageMap = new nsImageMap();
01516       if (mImageMap) {
01517         NS_ADDREF(mImageMap);
01518         mImageMap->Init(aPresContext->PresShell(), this, map);
01519       }
01520     }
01521   }
01522 
01523   return mImageMap;
01524 }
01525 
01526 void
01527 nsImageFrame::TriggerLink(nsPresContext* aPresContext,
01528                           nsIURI* aURI,
01529                           const nsString& aTargetSpec,
01530                           PRBool aClick)
01531 {
01532   // We get here with server side image map
01533   nsILinkHandler *handler = aPresContext->GetLinkHandler();
01534   if (handler) {
01535     if (aClick) {
01536       // Check that this page is allowed to load this URI.
01537       // Almost a copy of the similarly named method in nsGenericElement
01538       nsresult rv;
01539       nsCOMPtr<nsIScriptSecurityManager> securityManager = 
01540                do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
01541 
01542       if (NS_FAILED(rv))
01543         return;
01544 
01545       nsIPresShell *ps = aPresContext->GetPresShell();
01546       if (!ps)
01547         return;
01548 
01549       nsIDocument *doc = ps->GetDocument();
01550       if (doc) {
01551         rv = securityManager->
01552           CheckLoadURIWithPrincipal(doc->GetPrincipal(), aURI,
01553                                     nsIScriptSecurityManager::STANDARD);
01554 
01555         // Only pass off the click event if the script security manager
01556         // says it's ok.
01557         if (NS_SUCCEEDED(rv))
01558           handler->OnLinkClick(mContent, eLinkVerb_Replace, aURI,
01559                                aTargetSpec.get());
01560       }
01561     }
01562     else {
01563       handler->OnOverLink(mContent, aURI, aTargetSpec.get());
01564     }
01565   }
01566 }
01567 
01568 PRBool
01569 nsImageFrame::IsServerImageMap()
01570 {
01571   nsAutoString ismap;
01572   return NS_CONTENT_ATTR_HAS_VALUE ==
01573     mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::ismap, ismap);
01574 }
01575 
01576 //XXX the event come's in in view relative coords, but really should
01577 //be in frame relative coords by the time it hits our frame.
01578 
01579 // Translate an point that is relative to our view (or a containing
01580 // view) into a localized pixel coordinate that is relative to the
01581 // content area of this frame (inside the border+padding).
01582 void
01583 nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
01584                                          nsPoint& aResult)
01585 {
01586   nscoord x = aPoint.x;
01587   nscoord y = aPoint.y;
01588 
01589   // If we have a view then the event coordinates are already relative
01590   // to this frame; otherwise we have to adjust the coordinates
01591   // appropriately.
01592   if (!HasView()) {
01593     nsPoint offset;
01594     nsIView *view;
01595     GetOffsetFromView(offset, &view);
01596     if (nsnull != view) {
01597       x -= offset.x;
01598       y -= offset.y;
01599     }
01600   }
01601 
01602   // Subtract out border and padding here so that the coordinates are
01603   // now relative to the content area of this frame.
01604   nsRect inner = GetInnerArea();
01605   x -= inner.x;
01606   y -= inner.y;
01607 
01608   // Translate the coordinates from twips to pixels
01609   float t2p;
01610   t2p = GetPresContext()->TwipsToPixels();
01611   aResult.x = NSTwipsToIntPixels(x, t2p);
01612   aResult.y = NSTwipsToIntPixels(y, t2p);
01613 }
01614 
01615 PRBool
01616 nsImageFrame::GetAnchorHREFAndTarget(nsIURI** aHref, nsString& aTarget)
01617 {
01618   PRBool status = PR_FALSE;
01619   aTarget.Truncate();
01620 
01621   // Walk up the content tree, looking for an nsIDOMAnchorElement
01622   for (nsIContent* content = mContent->GetParent();
01623        content; content = content->GetParent()) {
01624     nsCOMPtr<nsILink> link(do_QueryInterface(content));
01625     if (link) {
01626       link->GetHrefURI(aHref);
01627       status = (*aHref != nsnull);
01628 
01629       nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
01630       if (anchor) {
01631         anchor->GetTarget(aTarget);
01632       }
01633       break;
01634     }
01635   }
01636   return status;
01637 }
01638 
01639 NS_IMETHODIMP
01640 nsImageFrame::CanContinueTextRun(PRBool& aContinueTextRun) const
01641 {
01642   // images really CAN continue text runs, but the textFrame needs to be 
01643   // educated before we can indicate that it can. For now, we handle the fixing up 
01644   // of max element widths in nsLineLayout::VerticalAlignFrames, but hopefully
01645   // this can be eliminated and the textFrame can be convinced to handle inlines
01646   // that take up space in text runs.
01647 
01648   aContinueTextRun = PR_FALSE;
01649   return NS_OK;
01650 }
01651 
01652 
01653 NS_IMETHODIMP  
01654 nsImageFrame::GetContentForEvent(nsPresContext* aPresContext,
01655                                  nsEvent* aEvent,
01656                                  nsIContent** aContent)
01657 {
01658   NS_ENSURE_ARG_POINTER(aContent);
01659   nsImageMap* map;
01660   map = GetImageMap(aPresContext);
01661 
01662   if (nsnull != map) {
01663     nsPoint p;
01664     TranslateEventCoords(aEvent->point, p);
01665     PRBool inside = PR_FALSE;
01666     nsCOMPtr<nsIContent> area;
01667     inside = map->IsInside(p.x, p.y, getter_AddRefs(area));
01668     if (inside && area) {
01669       *aContent = area;
01670       NS_ADDREF(*aContent);
01671       return NS_OK;
01672     }
01673   }
01674 
01675   *aContent = GetContent();
01676   NS_IF_ADDREF(*aContent);
01677   return NS_OK;
01678 }
01679 
01680 // XXX what should clicks on transparent pixels do?
01681 NS_IMETHODIMP
01682 nsImageFrame::HandleEvent(nsPresContext* aPresContext,
01683                           nsGUIEvent* aEvent,
01684                           nsEventStatus* aEventStatus)
01685 {
01686   NS_ENSURE_ARG_POINTER(aEventStatus);
01687   nsImageMap* map;
01688 
01689   switch (aEvent->message) {
01690   case NS_MOUSE_LEFT_BUTTON_UP:
01691   case NS_MOUSE_MOVE:
01692     {
01693       map = GetImageMap(aPresContext);
01694       PRBool isServerMap = IsServerImageMap();
01695       if ((nsnull != map) || isServerMap) {
01696         nsPoint p;
01697         TranslateEventCoords(aEvent->point, p);
01698         PRBool inside = PR_FALSE;
01699         // Even though client-side image map triggering happens
01700         // through content, we need to make sure we're not inside
01701         // (in case we deal with a case of both client-side and
01702         // sever-side on the same image - it happens!)
01703         if (nsnull != map) {
01704           nsCOMPtr<nsIContent> area;
01705           inside = map->IsInside(p.x, p.y, getter_AddRefs(area));
01706         }
01707 
01708         if (!inside && isServerMap) {
01709 
01710           // Server side image maps use the href in a containing anchor
01711           // element to provide the basis for the destination url.
01712           nsCOMPtr<nsIURI> uri;
01713           nsAutoString target;
01714           if (GetAnchorHREFAndTarget(getter_AddRefs(uri), target)) {
01715             // XXX if the mouse is over/clicked in the border/padding area
01716             // we should probably just pretend nothing happened. Nav4
01717             // keeps the x,y coordinates positive as we do; IE doesn't
01718             // bother. Both of them send the click through even when the
01719             // mouse is over the border.
01720             if (p.x < 0) p.x = 0;
01721             if (p.y < 0) p.y = 0;
01722             nsCAutoString spec;
01723             uri->GetSpec(spec);
01724             spec += nsPrintfCString("?%d,%d", p.x, p.y);
01725             uri->SetSpec(spec);                
01726             
01727             PRBool clicked = PR_FALSE;
01728             if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
01729               *aEventStatus = nsEventStatus_eConsumeDoDefault; 
01730               clicked = PR_TRUE;
01731             }
01732             TriggerLink(aPresContext, uri, target, clicked);
01733           }
01734         }
01735       }
01736       break;
01737     }
01738     default:
01739       break;
01740   }
01741 
01742   return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
01743 }
01744 
01745 NS_IMETHODIMP
01746 nsImageFrame::GetCursor(const nsPoint& aPoint,
01747                         nsIFrame::Cursor& aCursor)
01748 {
01749   nsPresContext* context = GetPresContext();
01750   nsImageMap* map = GetImageMap(context);
01751   if (nsnull != map) {
01752     nsPoint p;
01753     TranslateEventCoords(aPoint, p);
01754     nsCOMPtr<nsIContent> area;
01755     if (map->IsInside(p.x, p.y, getter_AddRefs(area))) {
01756       // Use the cursor from the style of the *area* element.
01757       // XXX Using the image as the parent style context isn't
01758       // technically correct, but it's probably the right thing to do
01759       // here, since it means that areas on which the cursor isn't
01760       // specified will inherit the style from the image.
01761       nsRefPtr<nsStyleContext> areaStyle = 
01762         GetPresContext()->PresShell()->StyleSet()->
01763           ResolveStyleFor(area, GetStyleContext());
01764       if (areaStyle) {
01765         FillCursorInformationFromStyle(areaStyle->GetStyleUserInterface(),
01766                                        aCursor);
01767         if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
01768           aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
01769         }
01770         return NS_OK;
01771       }
01772     }
01773   }
01774   return nsFrame::GetCursor(aPoint, aCursor);
01775 }
01776 
01777 NS_IMETHODIMP
01778 nsImageFrame::AttributeChanged(nsIContent* aChild,
01779                                PRInt32 aNameSpaceID,
01780                                nsIAtom* aAttribute,
01781                                PRInt32 aModType)
01782 {
01783   nsresult rv = nsSplittableFrame::AttributeChanged(aChild, aNameSpaceID,
01784                                                     aAttribute, aModType);
01785   if (NS_FAILED(rv)) {
01786     return rv;
01787   }
01788   if (nsHTMLAtoms::alt == aAttribute)
01789   {
01790     mState |= NS_FRAME_IS_DIRTY;
01791     mParent->ReflowDirtyChild(GetPresContext()->PresShell(), (nsIFrame*) this);
01792   }
01793 
01794   return NS_OK;
01795 }
01796 
01797 nsIAtom*
01798 nsImageFrame::GetType() const
01799 {
01800   return nsLayoutAtoms::imageFrame;
01801 }
01802 
01803 #ifdef DEBUG
01804 NS_IMETHODIMP
01805 nsImageFrame::GetFrameName(nsAString& aResult) const
01806 {
01807   return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
01808 }
01809 
01810 NS_IMETHODIMP
01811 nsImageFrame::List(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent) const
01812 {
01813   IndentBy(out, aIndent);
01814   ListTag(out);
01815 #ifdef DEBUG_waterson
01816   fprintf(out, " [parent=%p]", mParent);
01817 #endif
01818   if (HasView()) {
01819     fprintf(out, " [view=%p]", GetView());
01820   }
01821   fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, 
01822 mRect.height);
01823   if (0 != mState) {
01824     fprintf(out, " [state=%08x]", mState);
01825   }
01826   fprintf(out, " [content=%p]", mContent);
01827 
01828   // output the img src url
01829   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
01830   if (imageLoader) {
01831     nsCOMPtr<imgIRequest> currentRequest;
01832     imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
01833                             getter_AddRefs(currentRequest));
01834     if (currentRequest) {
01835       nsCOMPtr<nsIURI> uri;
01836       currentRequest->GetURI(getter_AddRefs(uri));
01837       nsCAutoString uristr;
01838       uri->GetAsciiSpec(uristr);
01839       fprintf(out, " [src=%s]", uristr.get());
01840     }
01841   }
01842   fputs("\n", out);
01843   return NS_OK;
01844 }
01845 #endif
01846 
01847 NS_IMETHODIMP 
01848 nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
01849 {
01850   aSize = mIntrinsicSize;
01851   return NS_OK;
01852 }
01853 
01854 nsresult
01855 nsImageFrame::LoadIcon(const nsAString& aSpec,
01856                        nsPresContext *aPresContext,
01857                        imgIRequest** aRequest)
01858 {
01859   nsresult rv = NS_OK;
01860   NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
01861 
01862   if (!sIOService) {
01863     static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
01864     rv = CallGetService(kIOServiceCID, &sIOService);
01865     NS_ENSURE_SUCCESS(rv, rv);
01866   }
01867 
01868   nsCOMPtr<nsIURI> realURI;
01869   SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
01870  
01871   nsCOMPtr<imgILoader> il(do_GetService("@mozilla.org/image/loader;1", &rv));
01872   if (NS_FAILED(rv)) return rv;
01873 
01874   nsCOMPtr<nsILoadGroup> loadGroup;
01875   GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
01876 
01877   // For icon loads, we don't need to merge with the loadgroup flags
01878   nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
01879 
01880   return il->LoadImage(realURI,     /* icon URI */
01881                        nsnull,      /* initial document URI; this is only
01882                                        relevant for cookies, so does not
01883                                        apply to icons. */
01884                        nsnull,      /* referrer (not relevant for icons) */
01885                        loadGroup,
01886                        mListener,
01887                        nsnull,      /* Not associated with any particular document */
01888                        loadFlags,
01889                        nsnull,
01890                        nsnull,
01891                        aRequest);
01892 }
01893 
01894 void
01895 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
01896 {
01897   if (mContent) {
01898     NS_ASSERTION(mContent->GetDocument(),
01899                  "Frame still alive after content removed from document!");
01900     aCharset = mContent->GetDocument()->GetDocumentCharacterSet();
01901   }
01902 }
01903 
01904 void
01905 nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
01906                          nsIURI **aURI)
01907 {
01908   nsCOMPtr<nsIURI> baseURI;
01909   if (mContent) {
01910     baseURI = mContent->GetBaseURI();
01911   }
01912   nsCAutoString charset;
01913   GetDocumentCharacterSet(charset);
01914   NS_NewURI(aURI, aSpec, 
01915             charset.IsEmpty() ? nsnull : charset.get(), 
01916             baseURI, aIOService);
01917 }
01918 
01919 void
01920 nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
01921 {
01922   if (!aPresContext)
01923     return;
01924 
01925   NS_PRECONDITION(nsnull != aLoadGroup, "null OUT parameter pointer");
01926 
01927   nsIPresShell *shell = aPresContext->GetPresShell();
01928 
01929   if (!shell)
01930     return;
01931 
01932   nsIDocument *doc = shell->GetDocument();
01933   if (!doc)
01934     return;
01935 
01936   *aLoadGroup = doc->GetDocumentLoadGroup().get();  // already_AddRefed
01937 }
01938 
01939 nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
01940 {
01941   NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
01942 
01943   NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre/res/loading-image.gif"); 
01944   NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre/res/broken-image.gif");
01945 
01946   gIconLoad = new IconLoad(mListener);
01947   if (!gIconLoad) 
01948     return NS_ERROR_OUT_OF_MEMORY;
01949   NS_ADDREF(gIconLoad);
01950 
01951   nsresult rv;
01952   // create a loader and load the images
01953   rv = LoadIcon(loadingSrc,
01954                 aPresContext,
01955                 getter_AddRefs(gIconLoad->mLoadingImage));
01956 #ifdef NOISY_ICON_LOADING
01957   printf("Loading request %p, rv=%u\n",
01958          gIconLoad->mLoadingImage.get(), rv);
01959 #endif
01960 
01961   if (NS_FAILED(rv)) {
01962     return rv;
01963   }
01964 
01965   rv = LoadIcon(brokenSrc,
01966                 aPresContext,
01967                 getter_AddRefs(gIconLoad->mBrokenImage));
01968 #ifdef NOISY_ICON_LOADING
01969   printf("Loading request %p, rv=%u\n",
01970          gIconLoad->mBrokenImage.get(), rv);
01971 #endif
01972 
01973   // ImageLoader will callback into OnStartContainer, which will
01974   // handle the mIconsLoaded flag
01975 
01976   return rv;
01977 }
01978 
01979 PRBool nsImageFrame::HandleIconLoads(imgIRequest* aRequest, PRBool aLoaded)
01980 {
01981   PRBool result = PR_FALSE;
01982 
01983   if (gIconLoad) {
01984     // check which image it is
01985     if (aRequest == gIconLoad->mLoadingImage ||
01986         aRequest == gIconLoad->mBrokenImage) {
01987       result = PR_TRUE;
01988       if (aLoaded && (++gIconLoad->mIconsLoaded == 2))
01989         gIconLoad->mLoadObserver = nsnull;
01990     }
01991 
01992 #ifdef NOISY_ICON_LOADING
01993     if (gIconLoad->mIconsLoaded && result) {
01994       printf( "Icons Loaded: request for %s\n",
01995               aRequest == gIconLoad->mLoadingImage
01996                 ? "mLoadingImage" : "mBrokenImage" );
01997     }
01998 #endif
01999   }
02000   
02001 #ifdef NOISY_ICON_LOADING
02002   printf( "HandleIconLoads returned %s (%p)\n", result ? "TRUE" : "FALSE", this);
02003 #endif
02004 
02005   return result;
02006 }
02007 
02008 void nsImageFrame::InvalidateIcon()
02009 {
02010   // invalidate the inner area, where the icon lives
02011 
02012   nsPresContext *presContext = GetPresContext();
02013   float   p2t = presContext->ScaledPixelsToTwips();
02014   nsRect inner = GetInnerArea();
02015 
02016   nsRect rect(inner.x,
02017               inner.y,
02018               NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING, p2t),
02019               NSIntPixelsToTwips(ICON_SIZE+ICON_PADDING, p2t));
02020   NS_ASSERTION(!rect.IsEmpty(), "icon rect cannot be empty!");
02021   // update image area
02022   Invalidate(rect, PR_FALSE);
02023 }
02024 
02025 NS_IMPL_ISUPPORTS1(nsImageFrame::IconLoad, nsIObserver)
02026 
02027 static const char kIconLoadPrefs[][40] = {
02028   "browser.display.force_inline_alttext",
02029   "browser.display.show_image_placeholders"
02030 };
02031 
02032 nsImageFrame::IconLoad::IconLoad(imgIDecoderObserver *aObserver)
02033   : mLoadObserver(aObserver),
02034     mIconsLoaded(0)
02035 {
02036   nsCOMPtr<nsIPrefBranch2> prefBranch =
02037     do_QueryInterface(nsContentUtils::GetPrefBranch());
02038 
02039   // register observers
02040   for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(kIconLoadPrefs); ++i)
02041     prefBranch->AddObserver(kIconLoadPrefs[i], this, PR_FALSE);
02042 
02043   GetPrefs();
02044 }
02045 
02046 
02047 NS_IMETHODIMP
02048 nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
02049                                 const PRUnichar* aData)
02050 {
02051   NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
02052                "wrong topic");
02053 #ifdef DEBUG
02054   // assert |aData| is one of our prefs.
02055   for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(kIconLoadPrefs) ||
02056                        (NS_NOTREACHED("wrong pref"), PR_FALSE); ++i)
02057     if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
02058       break;
02059 #endif
02060 
02061   GetPrefs();
02062   return NS_OK;
02063 }
02064 
02065 void nsImageFrame::IconLoad::GetPrefs()
02066 {
02067   mPrefForceInlineAltText =
02068     nsContentUtils::GetBoolPref("browser.display.force_inline_alttext");
02069 
02070   mPrefShowPlaceholders =
02071     nsContentUtils::GetBoolPref("browser.display.show_image_placeholders",
02072                                 PR_TRUE);
02073 }
02074 
02075 NS_IMPL_ISUPPORTS2(nsImageListener, imgIDecoderObserver, imgIContainerObserver)
02076 
02077 nsImageListener::nsImageListener(nsImageFrame *aFrame) :
02078   mFrame(aFrame)
02079 {
02080 }
02081 
02082 nsImageListener::~nsImageListener()
02083 {
02084 }
02085 
02086 NS_IMETHODIMP nsImageListener::OnStartDecode(imgIRequest *aRequest)
02087 {
02088   // Not useful to us yet.
02089   return NS_OK;
02090 }
02091 
02092 NS_IMETHODIMP nsImageListener::OnStartContainer(imgIRequest *aRequest,
02093                                                 imgIContainer *aImage)
02094 {
02095   if (!mFrame)
02096     return NS_ERROR_FAILURE;
02097 
02098   return mFrame->OnStartContainer(aRequest, aImage);
02099 }
02100 
02101 NS_IMETHODIMP nsImageListener::OnStartFrame(imgIRequest *aRequest,
02102                                             gfxIImageFrame *aFrame)
02103 {
02104   // Not useful to us yet.
02105   return NS_OK;
02106 }
02107 
02108 NS_IMETHODIMP nsImageListener::OnDataAvailable(imgIRequest *aRequest,
02109                                                gfxIImageFrame *aFrame,
02110                                                const nsRect *aRect)
02111 {
02112   if (!mFrame)
02113     return NS_ERROR_FAILURE;
02114 
02115   return mFrame->OnDataAvailable(aRequest, aFrame, aRect);
02116 }
02117 
02118 NS_IMETHODIMP nsImageListener::OnStopFrame(imgIRequest *aRequest,
02119                                            gfxIImageFrame *aFrame)
02120 {
02121   // Not useful to us yet.
02122   return NS_OK;
02123 }
02124 
02125 NS_IMETHODIMP nsImageListener::OnStopContainer(imgIRequest *aRequest,
02126                                                imgIContainer *aImage)
02127 {
02128   // Not useful to us yet.
02129   return NS_OK;
02130 }
02131 
02132 NS_IMETHODIMP nsImageListener::OnStopDecode(imgIRequest *aRequest,
02133                                             nsresult status,
02134                                             const PRUnichar *statusArg)
02135 {
02136   if (!mFrame)
02137     return NS_ERROR_FAILURE;
02138 
02139   return mFrame->OnStopDecode(aRequest, status, statusArg);
02140 }
02141 
02142 NS_IMETHODIMP nsImageListener::FrameChanged(imgIContainer *aContainer,
02143                                             gfxIImageFrame *newframe,
02144                                             nsRect * dirtyRect)
02145 {
02146   if (!mFrame)
02147     return NS_ERROR_FAILURE;
02148 
02149   return mFrame->FrameChanged(aContainer, newframe, dirtyRect);
02150 }
02151