layout/generic/nsImageFrame.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /* rendering object for replaced elements with bitmap image data */
     8 #include "nsImageFrame.h"
    10 #include "mozilla/DebugOnly.h"
    11 #include "mozilla/EventStates.h"
    12 #include "mozilla/MouseEvents.h"
    14 #include "nsCOMPtr.h"
    15 #include "nsIImageLoadingContent.h"
    16 #include "nsString.h"
    17 #include "nsPrintfCString.h"
    18 #include "nsPresContext.h"
    19 #include "nsRenderingContext.h"
    20 #include "nsIPresShell.h"
    21 #include "nsGkAtoms.h"
    22 #include "nsIDocument.h"
    23 #include "nsContentUtils.h"
    24 #include "nsCSSAnonBoxes.h"
    25 #include "nsStyleContext.h"
    26 #include "nsStyleConsts.h"
    27 #include "nsStyleCoord.h"
    28 #include "nsTransform2D.h"
    29 #include "nsImageMap.h"
    30 #include "nsIIOService.h"
    31 #include "nsILoadGroup.h"
    32 #include "nsISupportsPriority.h"
    33 #include "nsNetUtil.h"
    34 #include "nsCSSRendering.h"
    35 #include "nsIDOMHTMLAnchorElement.h"
    36 #include "nsNameSpaceManager.h"
    37 #include <algorithm>
    38 #ifdef ACCESSIBILITY
    39 #include "nsAccessibilityService.h"
    40 #endif
    41 #include "nsIDOMNode.h"
    42 #include "nsLayoutUtils.h"
    43 #include "nsDisplayList.h"
    45 #include "imgIContainer.h"
    46 #include "imgLoader.h"
    47 #include "imgRequestProxy.h"
    49 #include "nsCSSFrameConstructor.h"
    50 #include "nsIDOMRange.h"
    52 #include "nsError.h"
    53 #include "nsBidiUtils.h"
    54 #include "nsBidiPresUtils.h"
    55 #include "mozIThirdPartyUtil.h"
    57 #include "gfxRect.h"
    58 #include "ImageLayers.h"
    59 #include "ImageContainer.h"
    60 #include "nsStyleSet.h"
    61 #include "nsBlockFrame.h"
    62 #include "nsStyleStructInlines.h"
    64 #include "mozilla/Preferences.h"
    66 #include "mozilla/dom/Link.h"
    68 using namespace mozilla;
    70 // sizes (pixels) for image icon, padding and border frame
    71 #define ICON_SIZE        (16)
    72 #define ICON_PADDING     (3)
    73 #define ALT_BORDER_WIDTH (1)
    76 //we must add hooks soon
    77 #define IMAGE_EDITOR_CHECK 1
    79 // Default alignment value (so we can tell an unset value from a set value)
    80 #define ALIGN_UNSET uint8_t(-1)
    82 using namespace mozilla::layers;
    83 using namespace mozilla::dom;
    85 // static icon information
    86 nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr;
    88 // cached IO service for loading icons
    89 nsIIOService* nsImageFrame::sIOService;
    91 // test if the width and height are fixed, looking at the style data
    92 static bool HaveFixedSize(const nsStylePosition* aStylePosition)
    93 {
    94   // check the width and height values in the reflow state's style struct
    95   // - if width and height are specified as either coord or percentage, then
    96   //   the size of the image frame is constrained
    97   return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
    98          aStylePosition->mHeight.IsCoordPercentCalcUnit();
    99 }
   100 // use the data in the reflow state to decide if the image has a constrained size
   101 // (i.e. width and height that are based on the containing block size and not the image size) 
   102 // so we can avoid animated GIF related reflows
   103 inline bool HaveFixedSize(const nsHTMLReflowState& aReflowState)
   104 { 
   105   NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition");
   106   // when an image has percent css style height or width, but ComputedHeight() 
   107   // or ComputedWidth() of reflow state is  NS_UNCONSTRAINEDSIZE  
   108   // it needs to return false to cause an incremental reflow later
   109   // if an image is inside table like bug 156731 simple testcase III, 
   110   // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE
   111   // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false
   112   // see bug 156731
   113   const nsStyleCoord &height = aReflowState.mStylePosition->mHeight;
   114   const nsStyleCoord &width = aReflowState.mStylePosition->mWidth;
   115   return ((height.HasPercent() &&
   116            NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) ||
   117           (width.HasPercent() &&
   118            (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() ||
   119             0 == aReflowState.ComputedWidth())))
   120           ? false
   121           : HaveFixedSize(aReflowState.mStylePosition); 
   122 }
   124 nsIFrame*
   125 NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   126 {
   127   return new (aPresShell) nsImageFrame(aContext);
   128 }
   130 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
   133 nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
   134   ImageFrameSuper(aContext),
   135   mComputedSize(0, 0),
   136   mIntrinsicRatio(0, 0),
   137   mDisplayingIcon(false),
   138   mFirstFrameComplete(false),
   139   mReflowCallbackPosted(false)
   140 {
   141   // We assume our size is not constrained and we haven't gotten an
   142   // initial reflow yet, so don't touch those flags.
   143   mIntrinsicSize.width.SetCoordValue(0);
   144   mIntrinsicSize.height.SetCoordValue(0);
   145 }
   147 nsImageFrame::~nsImageFrame()
   148 {
   149 }
   151 NS_QUERYFRAME_HEAD(nsImageFrame)
   152   NS_QUERYFRAME_ENTRY(nsImageFrame)
   153 NS_QUERYFRAME_TAIL_INHERITING(ImageFrameSuper)
   155 #ifdef ACCESSIBILITY
   156 a11y::AccType
   157 nsImageFrame::AccessibleType()
   158 {
   159   // Don't use GetImageMap() to avoid reentrancy into accessibility.
   160   if (HasImageMap()) {
   161     return a11y::eHTMLImageMapType;
   162   }
   164   return a11y::eImageType;
   165 }
   166 #endif
   168 void
   169 nsImageFrame::DisconnectMap()
   170 {
   171   if (mImageMap) {
   172     mImageMap->Destroy();
   173     NS_RELEASE(mImageMap);
   175 #ifdef ACCESSIBILITY
   176   nsAccessibilityService* accService = GetAccService();
   177   if (accService) {
   178     accService->RecreateAccessible(PresContext()->PresShell(), mContent);
   179   }
   180 #endif
   181   }
   182 }
   184 void
   185 nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
   186 {
   187   if (mReflowCallbackPosted) {
   188     PresContext()->PresShell()->CancelReflowCallback(this);
   189     mReflowCallbackPosted = false;
   190   }
   192   // Tell our image map, if there is one, to clean up
   193   // This causes the nsImageMap to unregister itself as
   194   // a DOM listener.
   195   DisconnectMap();
   197   // set the frame to null so we don't send messages to a dead object.
   198   if (mListener) {
   199     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   200     if (imageLoader) {
   201       // Notify our image loading content that we are going away so it can
   202       // deregister with our refresh driver.
   203       imageLoader->FrameDestroyed(this);
   205       imageLoader->RemoveObserver(mListener);
   206     }
   208     reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr);
   209   }
   211   mListener = nullptr;
   213   // If we were displaying an icon, take ourselves off the list
   214   if (mDisplayingIcon)
   215     gIconLoad->RemoveIconObserver(this);
   217   nsSplittableFrame::DestroyFrom(aDestructRoot);
   218 }
   222 void
   223 nsImageFrame::Init(nsIContent*      aContent,
   224                    nsIFrame*        aParent,
   225                    nsIFrame*        aPrevInFlow)
   226 {
   227   nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
   229   mListener = new nsImageListener(this);
   231   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
   232   if (!imageLoader) {
   233     NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
   234   }
   236   imageLoader->AddObserver(mListener);
   238   nsPresContext *aPresContext = PresContext();
   240   if (!gIconLoad)
   241     LoadIcons(aPresContext);
   243   // We have a PresContext now, so we need to notify the image content node
   244   // that it can register images.
   245   imageLoader->FrameCreated(this);
   247   // Give image loads associated with an image frame a small priority boost!
   248   nsCOMPtr<imgIRequest> currentRequest;
   249   imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   250                           getter_AddRefs(currentRequest));
   251   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
   252   if (p)
   253     p->AdjustPriority(-1);
   255   // If we already have an image container, OnStartContainer won't be called
   256   if (currentRequest) {
   257     nsCOMPtr<imgIContainer> image;
   258     currentRequest->GetImage(getter_AddRefs(image));
   259     OnStartContainer(currentRequest, image);
   260   }
   261 }
   263 bool
   264 nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
   265 {
   266   NS_PRECONDITION(aImage, "null image");
   267   if (!aImage)
   268     return false;
   270   IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
   271   mIntrinsicSize = IntrinsicSize();
   273   // Set intrinsic size to match aImage's reported intrinsic width & height.
   274   nsSize intrinsicSize;
   275   if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
   276     // If the image has no intrinsic width, intrinsicSize.width will be -1, and
   277     // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
   278     // Otherwise we use intrinsicSize.width. Height works the same way.
   279     if (intrinsicSize.width != -1)
   280       mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
   281     if (intrinsicSize.height != -1)
   282       mIntrinsicSize.height.SetCoordValue(intrinsicSize.height);
   283   } else {
   284     // Failure means that the image hasn't loaded enough to report a result. We
   285     // treat this case as if the image's intrinsic size was 0x0.
   286     mIntrinsicSize.width.SetCoordValue(0);
   287     mIntrinsicSize.height.SetCoordValue(0);
   288   }
   290   return mIntrinsicSize != oldIntrinsicSize;
   291 }
   293 bool
   294 nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
   295 {
   296   NS_PRECONDITION(aImage, "null image");
   298   if (!aImage)
   299     return false;
   301   nsSize oldIntrinsicRatio = mIntrinsicRatio;
   303   // Set intrinsic ratio to match aImage's reported intrinsic ratio.
   304   if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
   305     mIntrinsicRatio.SizeTo(0, 0);
   307   return mIntrinsicRatio != oldIntrinsicRatio;
   308 }
   310 bool
   311 nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
   312 {
   313   // Set the translation components.
   314   // XXXbz does this introduce rounding errors because of the cast to
   315   // float?  Should we just manually add that stuff in every time
   316   // instead?
   317   nsRect innerArea = GetInnerArea();
   318   aTransform.SetToTranslate(float(innerArea.x),
   319                             float(innerArea.y - GetContinuationOffset()));
   321   // Set the scale factors.
   322   if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
   323       mIntrinsicSize.width.GetCoordValue() != 0 &&
   324       mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
   325       mIntrinsicSize.height.GetCoordValue() != 0 &&
   326       mIntrinsicSize.width.GetCoordValue() != mComputedSize.width &&
   327       mIntrinsicSize.height.GetCoordValue() != mComputedSize.height) {
   329     aTransform.SetScale(float(mComputedSize.width)  /
   330                         float(mIntrinsicSize.width.GetCoordValue()),
   331                         float(mComputedSize.height) /
   332                         float(mIntrinsicSize.height.GetCoordValue()));
   333     return true;
   334   }
   336   return false;
   337 }
   339 /*
   340  * These two functions basically do the same check.  The first one
   341  * checks that the given request is the current request for our
   342  * mContent.  The second checks that the given image container the
   343  * same as the image container on the current request for our
   344  * mContent.
   345  */
   346 bool
   347 nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
   348 {
   349   // Default to pending load in case of errors
   350   nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
   351   NS_ASSERTION(imageLoader, "No image loading content?");
   353   int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
   354   imageLoader->GetRequestType(aRequest, &requestType);
   356   return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
   357 }
   359 bool
   360 nsImageFrame::IsPendingLoad(imgIContainer* aContainer) const
   361 {
   362   //  default to pending load in case of errors
   363   if (!aContainer) {
   364     NS_ERROR("No image container!");
   365     return true;
   366   }
   368   nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
   369   NS_ASSERTION(imageLoader, "No image loading content?");
   371   nsCOMPtr<imgIRequest> currentRequest;
   372   imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   373                           getter_AddRefs(currentRequest));
   374   if (!currentRequest) {
   375     NS_ERROR("No current request");
   376     return true;
   377   }
   379   nsCOMPtr<imgIContainer> currentContainer;
   380   currentRequest->GetImage(getter_AddRefs(currentContainer));
   382   return currentContainer != aContainer;
   384 }
   386 nsRect
   387 nsImageFrame::SourceRectToDest(const nsIntRect& aRect)
   388 {
   389   // When scaling the image, row N of the source image may (depending on
   390   // the scaling function) be used to draw any row in the destination image
   391   // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
   392   // floating-point scaling factor.  The same holds true for columns.
   393   // So, we start by computing that bound without the floor and ceiling.
   395   nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
   396            nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
   397            nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
   398            nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
   400   nsTransform2D sourceToDest;
   401   if (!GetSourceToDestTransform(sourceToDest)) {
   402     // Failed to generate transform matrix. Return our whole inner area,
   403     // to be on the safe side (since this method is used for generating
   404     // invalidation rects).
   405     return GetInnerArea();
   406   }
   408   sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
   410   // Now, round the edges out to the pixel boundary.
   411   nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
   412   nscoord right = r.x + r.width;
   413   nscoord bottom = r.y + r.height;
   415   r.x -= (scale + (r.x % scale)) % scale;
   416   r.y -= (scale + (r.y % scale)) % scale;
   417   r.width = right + ((scale - (right % scale)) % scale) - r.x;
   418   r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
   420   return r;
   421 }
   423 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK".  This means
   424 // that we'll construct image frames for them as needed if their display is
   425 // toggled from "none" (though we won't paint them, unless their visibility
   426 // is changed too).
   427 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
   428                     NS_EVENT_STATE_LOADING)
   430 // This is a macro so that we don't evaluate the boolean last arg
   431 // unless we have to; it can be expensive
   432 #define IMAGE_OK(_state, _loadingOK)                                           \
   433    (!(_state).HasAtLeastOneOfStates(BAD_STATES) ||                                    \
   434     (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
   435      (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
   437 /* static */
   438 bool
   439 nsImageFrame::ShouldCreateImageFrameFor(Element* aElement,
   440                                         nsStyleContext* aStyleContext)
   441 {
   442   EventStates state = aElement->State();
   443   if (IMAGE_OK(state,
   444                HaveFixedSize(aStyleContext->StylePosition()))) {
   445     // Image is fine; do the image frame thing
   446     return true;
   447   }
   449   // Check if we want to use a placeholder box with an icon or just
   450   // let the presShell make us into inline text.  Decide as follows:
   451   //
   452   //  - if our special "force icons" style is set, show an icon
   453   //  - else if our "do not show placeholders" pref is set, skip the icon
   454   //  - else:
   455   //  - if there is a src attribute, there is no alt attribute,
   456   //    and this is not an <object> (which could not possibly have
   457   //    such an attribute), show an icon.
   458   //  - if QuirksMode, and the IMG has a size show an icon.
   459   //  - otherwise, skip the icon
   460   bool useSizedBox;
   462   if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) {
   463     useSizedBox = true;
   464   }
   465   else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
   466     useSizedBox = false;
   467   }
   468   else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
   469            !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
   470            !aElement->IsHTML(nsGkAtoms::object) &&
   471            !aElement->IsHTML(nsGkAtoms::input)) {
   472     // Use a sized box if we have no alt text.  This means no alt attribute
   473     // and the node is not an object or an input (since those always have alt
   474     // text).
   475     useSizedBox = true;
   476   }
   477   else if (aStyleContext->PresContext()->CompatibilityMode() !=
   478            eCompatibility_NavQuirks) {
   479     useSizedBox = false;
   480   }
   481   else {
   482     // check whether we have fixed size
   483     useSizedBox = HaveFixedSize(aStyleContext->StylePosition());
   484   }
   486   return useSizedBox;
   487 }
   489 nsresult
   490 nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
   491 {
   492   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
   493     nsCOMPtr<imgIContainer> image;
   494     aRequest->GetImage(getter_AddRefs(image));
   495     return OnStartContainer(aRequest, image);
   496   }
   498   if (aType == imgINotificationObserver::FRAME_UPDATE) {
   499     return OnDataAvailable(aRequest, aData);
   500   }
   502   if (aType == imgINotificationObserver::FRAME_COMPLETE) {
   503     mFirstFrameComplete = true;
   504   }
   506   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
   507     uint32_t imgStatus;
   508     aRequest->GetImageStatus(&imgStatus);
   509     nsresult status =
   510         imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
   511     return OnStopRequest(aRequest, status);
   512   }
   514   return NS_OK;
   515 }
   517 static bool
   518 SizeIsAvailable(imgIRequest* aRequest)
   519 {
   520   if (!aRequest)
   521     return false;
   523   uint32_t imageStatus = 0;
   524   nsresult rv = aRequest->GetImageStatus(&imageStatus);
   526   return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE); 
   527 }
   529 nsresult
   530 nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage)
   531 {
   532   if (!aImage) return NS_ERROR_INVALID_ARG;
   534   /* Get requested animation policy from the pres context:
   535    *   normal = 0
   536    *   one frame = 1
   537    *   one loop = 2
   538    */
   539   nsPresContext *presContext = PresContext();
   540   aImage->SetAnimationMode(presContext->ImageAnimationMode());
   542   if (IsPendingLoad(aRequest)) {
   543     // We don't care
   544     return NS_OK;
   545   }
   547   bool intrinsicSizeChanged = false;
   548   if (SizeIsAvailable(aRequest)) {
   549     // This is valid and for the current request, so update our stored image
   550     // container, orienting according to our style.
   551     mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
   553     intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
   554     intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
   555   } else {
   556     // We no longer have a valid image, so release our stored image container.
   557     mImage = nullptr;
   559     // Have to size to 0,0 so that GetDesiredSize recalculates the size.
   560     mIntrinsicSize.width.SetCoordValue(0);
   561     mIntrinsicSize.height.SetCoordValue(0);
   562     mIntrinsicRatio.SizeTo(0, 0);
   563     intrinsicSizeChanged = true;
   564   }
   566   if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) {
   567     // Now we need to reflow if we have an unconstrained size and have
   568     // already gotten the initial reflow
   569     if (!(mState & IMAGE_SIZECONSTRAINED)) { 
   570       nsIPresShell *presShell = presContext->GetPresShell();
   571       NS_ASSERTION(presShell, "No PresShell.");
   572       if (presShell) { 
   573         presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
   574                                     NS_FRAME_IS_DIRTY);
   575       }
   576     }
   577   }
   579   return NS_OK;
   580 }
   582 nsresult
   583 nsImageFrame::OnDataAvailable(imgIRequest *aRequest,
   584                               const nsIntRect *aRect)
   585 {
   586   if (mFirstFrameComplete) {
   587     nsCOMPtr<imgIContainer> container;
   588     aRequest->GetImage(getter_AddRefs(container));
   589     return FrameChanged(aRequest, container);
   590   }
   592   // XXX do we need to make sure that the reflow from the
   593   // OnStartContainer has been processed before we start calling
   594   // invalidate?
   596   NS_ENSURE_ARG_POINTER(aRect);
   598   if (!(mState & IMAGE_GOTINITIALREFLOW)) {
   599     // Don't bother to do anything; we have a reflow coming up!
   600     return NS_OK;
   601   }
   603   if (IsPendingLoad(aRequest)) {
   604     // We don't care
   605     return NS_OK;
   606   }
   608 #ifdef DEBUG_decode
   609   printf("Source rect (%d,%d,%d,%d)\n",
   610          aRect->x, aRect->y, aRect->width, aRect->height);
   611 #endif
   613   if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
   614     InvalidateFrame(nsDisplayItem::TYPE_IMAGE);
   615     InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK);
   616   } else {
   617     nsRect invalid = SourceRectToDest(*aRect);
   618     InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE);
   619     InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK);
   620   }
   622   return NS_OK;
   623 }
   625 nsresult
   626 nsImageFrame::OnStopRequest(imgIRequest *aRequest,
   627                             nsresult aStatus)
   628 {
   629   // Check what request type we're dealing with
   630   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   631   NS_ASSERTION(imageLoader, "Who's notifying us??");
   632   int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
   633   imageLoader->GetRequestType(aRequest, &loadType);
   634   if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
   635       loadType != nsIImageLoadingContent::PENDING_REQUEST) {
   636     return NS_ERROR_FAILURE;
   637   }
   639   NotifyNewCurrentRequest(aRequest, aStatus);
   640   return NS_OK;
   641 }
   643 void
   644 nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
   645                                       nsresult aStatus)
   646 {
   647   nsCOMPtr<imgIContainer> image;
   648   aRequest->GetImage(getter_AddRefs(image));
   649   NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
   651   // May have to switch sizes here!
   652   bool intrinsicSizeChanged = true;
   653   if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
   654     // Update our stored image container, orienting according to our style.
   655     mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
   657     intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
   658     intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
   659   } else {
   660     // We no longer have a valid image, so release our stored image container.
   661     mImage = nullptr;
   663     // Have to size to 0,0 so that GetDesiredSize recalculates the size
   664     mIntrinsicSize.width.SetCoordValue(0);
   665     mIntrinsicSize.height.SetCoordValue(0);
   666     mIntrinsicRatio.SizeTo(0, 0);
   667   }
   669   if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet
   670     if (!(mState & IMAGE_SIZECONSTRAINED) && intrinsicSizeChanged) {
   671       nsIPresShell *presShell = PresContext()->GetPresShell();
   672       if (presShell) { 
   673         presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
   674                                     NS_FRAME_IS_DIRTY);
   675       }
   676     }
   677     // Update border+content to account for image change
   678     InvalidateFrame();
   679   }
   680 }
   682 nsresult
   683 nsImageFrame::FrameChanged(imgIRequest *aRequest,
   684                            imgIContainer *aContainer)
   685 {
   686   if (!StyleVisibility()->IsVisible()) {
   687     return NS_OK;
   688   }
   690   if (IsPendingLoad(aContainer)) {
   691     // We don't care about it
   692     return NS_OK;
   693   }
   695   InvalidateLayer(nsDisplayItem::TYPE_IMAGE);
   696   return NS_OK;
   697 }
   699 void
   700 nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext)
   701 {
   702   // If mIntrinsicSize.width and height are 0, then we need to update from the
   703   // image container.
   704   if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
   705       mIntrinsicSize.width.GetCoordValue() == 0 &&
   706       mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
   707       mIntrinsicSize.height.GetCoordValue() == 0) {
   709     if (mImage) {
   710       UpdateIntrinsicSize(mImage);
   711       UpdateIntrinsicRatio(mImage);
   712     } else {
   713       // image request is null or image size not known, probably an
   714       // invalid image specified
   715       // - make the image big enough for the icon (it may not be
   716       // used if inline alt expansion is used instead)
   717       if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
   718         nscoord edgeLengthToUse =
   719           nsPresContext::CSSPixelsToAppUnits(
   720             ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
   721         mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
   722         mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
   723         mIntrinsicRatio.SizeTo(1, 1);
   724       }
   725     }
   726   }
   727 }
   729 /* virtual */ nsSize
   730 nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext,
   731                           nsSize aCBSize, nscoord aAvailableWidth,
   732                           nsSize aMargin, nsSize aBorder, nsSize aPadding,
   733                           uint32_t aFlags)
   734 {
   735   nsPresContext *presContext = PresContext();
   736   EnsureIntrinsicSizeAndRatio(presContext);
   738   return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
   739                             aRenderingContext, this,
   740                             mIntrinsicSize, mIntrinsicRatio, aCBSize,
   741                             aMargin, aBorder, aPadding);
   742 }
   744 nsRect 
   745 nsImageFrame::GetInnerArea() const
   746 {
   747   return GetContentRect() - GetPosition();
   748 }
   750 // get the offset into the content area of the image where aImg starts if it is a continuation.
   751 nscoord 
   752 nsImageFrame::GetContinuationOffset() const
   753 {
   754   nscoord offset = 0;
   755   for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
   756     offset += f->GetContentRect().height;
   757   }
   758   NS_ASSERTION(offset >= 0, "bogus GetContentRect");
   759   return offset;
   760 }
   762 /* virtual */ nscoord
   763 nsImageFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
   764 {
   765   // XXX The caller doesn't account for constraints of the height,
   766   // min-height, and max-height properties.
   767   DebugOnly<nscoord> result;
   768   DISPLAY_MIN_WIDTH(this, result);
   769   nsPresContext *presContext = PresContext();
   770   EnsureIntrinsicSizeAndRatio(presContext);
   771   return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
   772     mIntrinsicSize.width.GetCoordValue() : 0;
   773 }
   775 /* virtual */ nscoord
   776 nsImageFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
   777 {
   778   // XXX The caller doesn't account for constraints of the height,
   779   // min-height, and max-height properties.
   780   DebugOnly<nscoord> result;
   781   DISPLAY_PREF_WIDTH(this, result);
   782   nsPresContext *presContext = PresContext();
   783   EnsureIntrinsicSizeAndRatio(presContext);
   784   // convert from normal twips to scaled twips (printing...)
   785   return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
   786     mIntrinsicSize.width.GetCoordValue() : 0;
   787 }
   789 /* virtual */ IntrinsicSize
   790 nsImageFrame::GetIntrinsicSize()
   791 {
   792   return mIntrinsicSize;
   793 }
   795 /* virtual */ nsSize
   796 nsImageFrame::GetIntrinsicRatio()
   797 {
   798   return mIntrinsicRatio;
   799 }
   801 nsresult
   802 nsImageFrame::Reflow(nsPresContext*          aPresContext,
   803                      nsHTMLReflowMetrics&     aMetrics,
   804                      const nsHTMLReflowState& aReflowState,
   805                      nsReflowStatus&          aStatus)
   806 {
   807   DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
   808   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   809   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
   810                   ("enter nsImageFrame::Reflow: availSize=%d,%d",
   811                   aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
   813   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
   815   aStatus = NS_FRAME_COMPLETE;
   817   // see if we have a frozen size (i.e. a fixed width and height)
   818   if (HaveFixedSize(aReflowState)) {
   819     mState |= IMAGE_SIZECONSTRAINED;
   820   } else {
   821     mState &= ~IMAGE_SIZECONSTRAINED;
   822   }
   824   // XXXldb These two bits are almost exact opposites (except in the
   825   // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
   826   if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
   827     mState |= IMAGE_GOTINITIALREFLOW;
   828   }
   830   mComputedSize = 
   831     nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
   833   aMetrics.Width() = mComputedSize.width;
   834   aMetrics.Height() = mComputedSize.height;
   836   // add borders and padding
   837   aMetrics.Width()  += aReflowState.ComputedPhysicalBorderPadding().LeftRight();
   838   aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().TopBottom();
   840   if (GetPrevInFlow()) {
   841     aMetrics.Width() = GetPrevInFlow()->GetSize().width;
   842     nscoord y = GetContinuationOffset();
   843     aMetrics.Height() -= y + aReflowState.ComputedPhysicalBorderPadding().top;
   844     aMetrics.Height() = std::max(0, aMetrics.Height());
   845   }
   848   // we have to split images if we are:
   849   //  in Paginated mode, we need to have a constrained height, and have a height larger than our available height
   850   uint32_t loadStatus = imgIRequest::STATUS_NONE;
   851   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   852   NS_ASSERTION(imageLoader, "No content node??");
   853   if (imageLoader) {
   854     nsCOMPtr<imgIRequest> currentRequest;
   855     imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   856                             getter_AddRefs(currentRequest));
   857     if (currentRequest) {
   858       currentRequest->GetImageStatus(&loadStatus);
   859     }
   860   }
   861   if (aPresContext->IsPaginated() &&
   862       ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
   863       NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() && 
   864       aMetrics.Height() > aReflowState.AvailableHeight()) { 
   865     // our desired height was greater than 0, so to avoid infinite
   866     // splitting, use 1 pixel as the min
   867     aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowState.AvailableHeight());
   868     aStatus = NS_FRAME_NOT_COMPLETE;
   869   }
   871   aMetrics.SetOverflowAreasToDesiredBounds();
   872   EventStates contentState = mContent->AsElement()->State();
   873   bool imageOK = IMAGE_OK(contentState, true);
   875   // Determine if the size is available
   876   bool haveSize = false;
   877   if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) {
   878     haveSize = true;
   879   }
   881   if (!imageOK || !haveSize) {
   882     nsRect altFeedbackSize(0, 0,
   883                            nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)),
   884                            nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)));
   885     // We include the altFeedbackSize in our visual overflow, but not in our
   886     // scrollable overflow, since it doesn't really need to be scrolled to
   887     // outside the image.
   888     static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
   889     nsRect& visualOverflow = aMetrics.VisualOverflow();
   890     visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
   891   }
   892   FinishAndStoreOverflow(&aMetrics);
   894   if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
   895     nsIPresShell* shell = PresContext()->PresShell();
   896     mReflowCallbackPosted = true;
   897     shell->PostReflowCallback(this);
   898   }
   900   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
   901                   ("exit nsImageFrame::Reflow: size=%d,%d",
   902                   aMetrics.Width(), aMetrics.Height()));
   903   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
   904   return NS_OK;
   905 }
   907 bool
   908 nsImageFrame::ReflowFinished()
   909 {
   910   mReflowCallbackPosted = false;
   912   nsLayoutUtils::UpdateImageVisibilityForFrame(this);
   914   return false;
   915 }
   917 void
   918 nsImageFrame::ReflowCallbackCanceled()
   919 {
   920   mReflowCallbackPosted = false;
   921 }
   923 // Computes the width of the specified string. aMaxWidth specifies the maximum
   924 // width available. Once this limit is reached no more characters are measured.
   925 // The number of characters that fit within the maximum width are returned in
   926 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
   927 // into the rendering context before this is called (for performance). MMP
   928 nscoord
   929 nsImageFrame::MeasureString(const char16_t*     aString,
   930                             int32_t              aLength,
   931                             nscoord              aMaxWidth,
   932                             uint32_t&            aMaxFit,
   933                             nsRenderingContext& aContext)
   934 {
   935   nscoord totalWidth = 0;
   936   aContext.SetTextRunRTL(false);
   937   nscoord spaceWidth = aContext.GetWidth(' ');
   939   aMaxFit = 0;
   940   while (aLength > 0) {
   941     // Find the next place we can line break
   942     uint32_t  len = aLength;
   943     bool      trailingSpace = false;
   944     for (int32_t i = 0; i < aLength; i++) {
   945       if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
   946         len = i;  // don't include the space when measuring
   947         trailingSpace = true;
   948         break;
   949       }
   950     }
   952     // Measure this chunk of text, and see if it fits
   953     nscoord width =
   954       nsLayoutUtils::GetStringWidth(this, &aContext, aString, len);
   955     bool    fits = (totalWidth + width) <= aMaxWidth;
   957     // If it fits on the line, or it's the first word we've processed then
   958     // include it
   959     if (fits || (0 == totalWidth)) {
   960       // New piece fits
   961       totalWidth += width;
   963       // If there's a trailing space then see if it fits as well
   964       if (trailingSpace) {
   965         if ((totalWidth + spaceWidth) <= aMaxWidth) {
   966           totalWidth += spaceWidth;
   967         } else {
   968           // Space won't fit. Leave it at the end but don't include it in
   969           // the width
   970           fits = false;
   971         }
   973         len++;
   974       }
   976       aMaxFit += len;
   977       aString += len;
   978       aLength -= len;
   979     }
   981     if (!fits) {
   982       break;
   983     }
   984   }
   985   return totalWidth;
   986 }
   988 // Formats the alt-text to fit within the specified rectangle. Breaks lines
   989 // between words if a word would extend past the edge of the rectangle
   990 void
   991 nsImageFrame::DisplayAltText(nsPresContext*      aPresContext,
   992                              nsRenderingContext& aRenderingContext,
   993                              const nsString&      aAltText,
   994                              const nsRect&        aRect)
   995 {
   996   // Set font and color
   997   aRenderingContext.SetColor(StyleColor()->mColor);
   998   nsRefPtr<nsFontMetrics> fm;
   999   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
  1000     nsLayoutUtils::FontSizeInflationFor(this));
  1001   aRenderingContext.SetFont(fm);
  1003   // Format the text to display within the formatting rect
  1005   nscoord maxAscent = fm->MaxAscent();
  1006   nscoord maxDescent = fm->MaxDescent();
  1007   nscoord height = fm->MaxHeight();
  1009   // XXX It would be nice if there was a way to have the font metrics tell
  1010   // use where to break the text given a maximum width. At a minimum we need
  1011   // to be able to get the break character...
  1012   const char16_t* str = aAltText.get();
  1013   int32_t          strLen = aAltText.Length();
  1014   nscoord          y = aRect.y;
  1016   if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
  1017     aPresContext->SetBidiEnabled();
  1020   // Always show the first line, even if we have to clip it below
  1021   bool firstLine = true;
  1022   while ((strLen > 0) && (firstLine || (y + maxDescent) < aRect.YMost())) {
  1023     // Determine how much of the text to display on this line
  1024     uint32_t  maxFit;  // number of characters that fit
  1025     nscoord strWidth = MeasureString(str, strLen, aRect.width, maxFit,
  1026                                      aRenderingContext);
  1028     // Display the text
  1029     nsresult rv = NS_ERROR_FAILURE;
  1031     if (aPresContext->BidiEnabled()) {
  1032       const nsStyleVisibility* vis = StyleVisibility();
  1033       if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
  1034         rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_RTL,
  1035                                          aPresContext, aRenderingContext,
  1036                                          aRenderingContext,
  1037                                          aRect.XMost() - strWidth, y + maxAscent);
  1038       else
  1039         rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_LTR,
  1040                                          aPresContext, aRenderingContext,
  1041                                          aRenderingContext,
  1042                                          aRect.x, y + maxAscent);
  1044     if (NS_FAILED(rv))
  1045       aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent);
  1047     // Move to the next line
  1048     str += maxFit;
  1049     strLen -= maxFit;
  1050     y += height;
  1051     firstLine = false;
  1055 struct nsRecessedBorder : public nsStyleBorder {
  1056   nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
  1057     : nsStyleBorder(aPresContext)
  1059     NS_FOR_CSS_SIDES(side) {
  1060       // Note: use SetBorderColor here because we want to make sure
  1061       // the "special" flags are unset.
  1062       SetBorderColor(side, NS_RGB(0, 0, 0));
  1063       mBorder.Side(side) = aBorderWidth;
  1064       // Note: use SetBorderStyle here because we want to affect
  1065       // mComputedBorder
  1066       SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
  1069 };
  1071 class nsDisplayAltFeedback : public nsDisplayItem {
  1072 public:
  1073   nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
  1074     : nsDisplayItem(aBuilder, aFrame) {}
  1076   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
  1077                            bool* aSnap) MOZ_OVERRIDE
  1079     *aSnap = false;
  1080     return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
  1083   virtual void Paint(nsDisplayListBuilder* aBuilder,
  1084                      nsRenderingContext* aCtx) MOZ_OVERRIDE
  1086     nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
  1087     EventStates state = f->GetContent()->AsElement()->State();
  1088     f->DisplayAltFeedback(*aCtx,
  1089                           mVisibleRect,
  1090                           IMAGE_OK(state, true)
  1091                              ? nsImageFrame::gIconLoad->mLoadingImage
  1092                              : nsImageFrame::gIconLoad->mBrokenImage,
  1093                           ToReferenceFrame());
  1097   NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
  1098 };
  1100 void
  1101 nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext,
  1102                                  const nsRect&        aDirtyRect,
  1103                                  imgIRequest*         aRequest,
  1104                                  nsPoint              aPt)
  1106   // We should definitely have a gIconLoad here.
  1107   NS_ABORT_IF_FALSE(gIconLoad, "How did we succeed in Init then?");
  1109   // Calculate the inner area
  1110   nsRect  inner = GetInnerArea() + aPt;
  1112   // Display a recessed one pixel border
  1113   nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
  1115   // if inner area is empty, then make it big enough for at least the icon
  1116   if (inner.IsEmpty()){
  1117     inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)),
  1118                  2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)));
  1121   // Make sure we have enough room to actually render the border within
  1122   // our frame bounds
  1123   if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
  1124     return;
  1127   // Paint the border
  1128   nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
  1129   nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext,
  1130                                              this, inner, inner,
  1131                                              recessedBorder, mStyleContext);
  1133   // Adjust the inner rect to account for the one pixel recessed border,
  1134   // and a six pixel padding on each edge
  1135   inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH), 
  1136                 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH));
  1137   if (inner.IsEmpty()) {
  1138     return;
  1141   // Clip so we don't render outside the inner rect
  1142   aRenderingContext.PushState();
  1143   aRenderingContext.IntersectClip(inner);
  1145   // Check if we should display image placeholders
  1146   if (gIconLoad->mPrefShowPlaceholders) {
  1147     const nsStyleVisibility* vis = StyleVisibility();
  1148     nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
  1150     bool iconUsed = false;
  1152     // If we weren't previously displaying an icon, register ourselves
  1153     // as an observer for load and animation updates and flag that we're
  1154     // doing so now.
  1155     if (aRequest && !mDisplayingIcon) {
  1156       gIconLoad->AddIconObserver(this);
  1157       mDisplayingIcon = true;
  1161     // If the icon in question is loaded and decoded, draw it
  1162     uint32_t imageStatus = 0;
  1163     if (aRequest)
  1164       aRequest->GetImageStatus(&imageStatus);
  1165     if (imageStatus & imgIRequest::STATUS_FRAME_COMPLETE) {
  1166       nsCOMPtr<imgIContainer> imgCon;
  1167       aRequest->GetImage(getter_AddRefs(imgCon));
  1168       NS_ABORT_IF_FALSE(imgCon, "Frame Complete, but no image container?");
  1169       nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
  1170                   inner.XMost() - size : inner.x,
  1171                   inner.y, size, size);
  1172       nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
  1173         nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
  1174         nullptr, imgIContainer::FLAG_NONE);
  1175       iconUsed = true;
  1178     // if we could not draw the icon, flag that we're waiting for it and
  1179     // just draw some graffiti in the mean time
  1180     if (!iconUsed) {
  1181       nscoord iconXPos = (vis->mDirection ==   NS_STYLE_DIRECTION_RTL) ?
  1182                          inner.XMost() - size : inner.x;
  1183       nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
  1184       aRenderingContext.DrawRect(iconXPos, inner.y,size,size);
  1185       aRenderingContext.PushState();
  1186       aRenderingContext.SetColor(NS_RGB(0xFF,0,0));
  1187       aRenderingContext.FillEllipse(size/2 + iconXPos, size/2 + inner.y,
  1188                                     size/2 - twoPX, size/2 - twoPX);
  1189       aRenderingContext.PopState();
  1192     // Reduce the inner rect by the width of the icon, and leave an
  1193     // additional ICON_PADDING pixels for padding
  1194     int32_t iconWidth = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
  1195     if (vis->mDirection != NS_STYLE_DIRECTION_RTL)
  1196       inner.x += iconWidth;
  1197     inner.width -= iconWidth;
  1200   // If there's still room, display the alt-text
  1201   if (!inner.IsEmpty()) {
  1202     nsIContent* content = GetContent();
  1203     if (content) {
  1204       nsXPIDLString altText;
  1205       nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(),
  1206                                                  altText);
  1207       DisplayAltText(PresContext(), aRenderingContext, altText, inner);
  1211   aRenderingContext.PopState();
  1214 #ifdef DEBUG
  1215 static void PaintDebugImageMap(nsIFrame* aFrame, nsRenderingContext* aCtx,
  1216      const nsRect& aDirtyRect, nsPoint aPt) {
  1217   nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
  1218   nsRect inner = f->GetInnerArea() + aPt;
  1220   aCtx->SetColor(NS_RGB(0, 0, 0));
  1221   aCtx->PushState();
  1222   aCtx->Translate(inner.TopLeft());
  1223   f->GetImageMap()->Draw(aFrame, *aCtx);
  1224   aCtx->PopState();
  1226 #endif
  1228 void
  1229 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
  1230                       nsRenderingContext* aCtx) {
  1231   uint32_t flags = imgIContainer::FLAG_NONE;
  1232   if (aBuilder->ShouldSyncDecodeImages()) {
  1233     flags |= imgIContainer::FLAG_SYNC_DECODE;
  1235   if (aBuilder->IsPaintingToWindow()) {
  1236     flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
  1238   static_cast<nsImageFrame*>(mFrame)->
  1239     PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
  1242 void
  1243 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  1244                                           const nsDisplayItemGeometry* aGeometry,
  1245                                           nsRegion* aInvalidRegion)
  1247   if (aBuilder->ShouldSyncDecodeImages() && mImage && !mImage->IsDecoded()) {
  1248     bool snap;
  1249     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
  1252   nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
  1255 already_AddRefed<ImageContainer>
  1256 nsDisplayImage::GetContainer(LayerManager* aManager,
  1257                              nsDisplayListBuilder* aBuilder)
  1259   nsRefPtr<ImageContainer> container;
  1260   nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
  1261   NS_ENSURE_SUCCESS(rv, nullptr);
  1262   return container.forget();
  1265 gfxRect
  1266 nsDisplayImage::GetDestRect()
  1268   int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
  1269   nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
  1271   nsRect dest = imageFrame->GetInnerArea() + ToReferenceFrame();
  1272   gfxRect destRect(dest.x, dest.y, dest.width, dest.height);
  1273   destRect.ScaleInverse(factor); 
  1275   return destRect;
  1278 LayerState
  1279 nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
  1280                               LayerManager* aManager,
  1281                               const ContainerLayerParameters& aParameters)
  1283   bool animated = false;
  1284   if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
  1285       mImage->GetType() != imgIContainer::TYPE_RASTER ||
  1286       NS_FAILED(mImage->GetAnimated(&animated)) ||
  1287       !animated) {
  1288     if (!aManager->IsCompositingCheap() ||
  1289         !nsLayoutUtils::GPUImageScalingEnabled()) {
  1290       return LAYER_NONE;
  1294   if (!animated) {
  1295     int32_t imageWidth;
  1296     int32_t imageHeight;
  1297     mImage->GetWidth(&imageWidth);
  1298     mImage->GetHeight(&imageHeight);
  1300     NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
  1302     gfxRect destRect = GetDestRect();
  1304     destRect.width *= aParameters.mXScale;
  1305     destRect.height *= aParameters.mYScale;
  1307     // Calculate the scaling factor for the frame.
  1308     gfxSize scale = gfxSize(destRect.width / imageWidth,
  1309                             destRect.height / imageHeight);
  1311     // If we are not scaling at all, no point in separating this into a layer.
  1312     if (scale.width == 1.0f && scale.height == 1.0f) {
  1313       return LAYER_NONE;
  1316     // If the target size is pretty small, no point in using a layer.
  1317     if (destRect.width * destRect.height < 64 * 64) {
  1318       return LAYER_NONE;
  1322   nsRefPtr<ImageContainer> container;
  1323   mImage->GetImageContainer(aManager, getter_AddRefs(container));
  1324   if (!container) {
  1325     return LAYER_NONE;
  1328   return LAYER_ACTIVE;
  1331 already_AddRefed<Layer>
  1332 nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
  1333                            LayerManager* aManager,
  1334                            const ContainerLayerParameters& aParameters)
  1336   nsRefPtr<ImageContainer> container;
  1337   nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
  1338   NS_ENSURE_SUCCESS(rv, nullptr);
  1340   nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
  1341     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
  1342   if (!layer) {
  1343     layer = aManager->CreateImageLayer();
  1344     if (!layer)
  1345       return nullptr;
  1347   layer->SetContainer(container);
  1348   ConfigureLayer(layer, aParameters.mOffset);
  1349   return layer.forget();
  1352 void
  1353 nsDisplayImage::ConfigureLayer(ImageLayer *aLayer, const nsIntPoint& aOffset)
  1355   aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
  1357   int32_t imageWidth;
  1358   int32_t imageHeight;
  1359   mImage->GetWidth(&imageWidth);
  1360   mImage->GetHeight(&imageHeight);
  1362   NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
  1364   const gfxRect destRect = GetDestRect();
  1366   gfx::Matrix transform;
  1367   gfxPoint p = destRect.TopLeft() + aOffset;
  1368   transform.Translate(p.x, p.y);
  1369   transform.Scale(destRect.Width()/imageWidth,
  1370                   destRect.Height()/imageHeight);
  1371   aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
  1372   aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight));
  1375 void
  1376 nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
  1377                          const nsRect& aDirtyRect, imgIContainer* aImage,
  1378                          uint32_t aFlags)
  1380   // Render the image into our content area (the area inside
  1381   // the borders and padding)
  1382   NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
  1383   nsRect inner = GetInnerArea() + aPt;
  1384   nsRect dest(inner.TopLeft(), mComputedSize);
  1385   dest.y -= GetContinuationOffset();
  1387   nsLayoutUtils::DrawSingleImage(&aRenderingContext, aImage,
  1388     nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
  1389     nullptr, aFlags);
  1391   nsImageMap* map = GetImageMap();
  1392   if (nullptr != map) {
  1393     aRenderingContext.PushState();
  1394     aRenderingContext.Translate(inner.TopLeft());
  1395     aRenderingContext.SetColor(NS_RGB(255, 255, 255));
  1396     aRenderingContext.SetLineStyle(nsLineStyle_kSolid);
  1397     map->Draw(this, aRenderingContext);
  1398     aRenderingContext.SetColor(NS_RGB(0, 0, 0));
  1399     aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
  1400     map->Draw(this, aRenderingContext);
  1401     aRenderingContext.PopState();
  1405 void
  1406 nsImageFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  1407                                const nsRect&           aDirtyRect,
  1408                                const nsDisplayListSet& aLists)
  1410   if (!IsVisibleForPainting(aBuilder))
  1411     return;
  1413   DisplayBorderBackgroundOutline(aBuilder, aLists);
  1415   DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
  1416     clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT);
  1418   if (mComputedSize.width != 0 && mComputedSize.height != 0) {
  1419     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
  1420     NS_ASSERTION(imageLoader, "Not an image loading content?");
  1422     nsCOMPtr<imgIRequest> currentRequest;
  1423     if (imageLoader) {
  1424       imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
  1425                               getter_AddRefs(currentRequest));
  1428     EventStates contentState = mContent->AsElement()->State();
  1429     bool imageOK = IMAGE_OK(contentState, true);
  1431     // XXX(seth): The SizeIsAvailable check here should not be necessary - the
  1432     // intention is that a non-null mImage means we have a size, but there is
  1433     // currently some code that violates this invariant.
  1434     if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) {
  1435       // No image yet, or image load failed. Draw the alt-text and an icon
  1436       // indicating the status
  1437       aLists.Content()->AppendNewToTop(new (aBuilder)
  1438         nsDisplayAltFeedback(aBuilder, this));
  1439     } else {
  1440       aLists.Content()->AppendNewToTop(new (aBuilder)
  1441         nsDisplayImage(aBuilder, this, mImage));
  1443       // If we were previously displaying an icon, we're not anymore
  1444       if (mDisplayingIcon) {
  1445         gIconLoad->RemoveIconObserver(this);
  1446         mDisplayingIcon = false;
  1449 #ifdef DEBUG
  1450       if (GetShowFrameBorders() && GetImageMap()) {
  1451         aLists.Outlines()->AppendNewToTop(new (aBuilder)
  1452           nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap",
  1453                            nsDisplayItem::TYPE_DEBUG_IMAGE_MAP));
  1455 #endif
  1459   if (ShouldDisplaySelection()) {
  1460     DisplaySelectionOverlay(aBuilder, aLists.Content(),
  1461                             nsISelectionDisplay::DISPLAY_IMAGES);
  1465 bool
  1466 nsImageFrame::ShouldDisplaySelection()
  1468   // XXX what on EARTH is this code for?
  1469   nsresult result;
  1470   nsPresContext* presContext = PresContext();
  1471   int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
  1472   if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
  1473     return false;//no need to check the blue border, we cannot be drawn selected
  1474 //insert hook here for image selection drawing
  1475 #if IMAGE_EDITOR_CHECK
  1476   //check to see if this frame is in an editor context
  1477   //isEditor check. this needs to be changed to have better way to check
  1478   if (displaySelection == nsISelectionDisplay::DISPLAY_ALL) 
  1480     nsCOMPtr<nsISelectionController> selCon;
  1481     result = GetSelectionController(presContext, getter_AddRefs(selCon));
  1482     if (NS_SUCCEEDED(result) && selCon)
  1484       nsCOMPtr<nsISelection> selection;
  1485       result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  1486       if (NS_SUCCEEDED(result) && selection)
  1488         int32_t rangeCount;
  1489         selection->GetRangeCount(&rangeCount);
  1490         if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
  1492           nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
  1493           if (parentContent)
  1495             int32_t thisOffset = parentContent->IndexOf(mContent);
  1496             nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent);
  1497             nsCOMPtr<nsIDOMNode> rangeNode;
  1498             int32_t rangeOffset;
  1499             nsCOMPtr<nsIDOMRange> range;
  1500             selection->GetRangeAt(0,getter_AddRefs(range));
  1501             if (range)
  1503               range->GetStartContainer(getter_AddRefs(rangeNode));
  1504               range->GetStartOffset(&rangeOffset);
  1506               if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset)
  1508                 range->GetEndContainer(getter_AddRefs(rangeNode));
  1509                 range->GetEndOffset(&rangeOffset);
  1510                 if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only
  1511                   return false; //do not allow nsFrame do draw any further selection
  1519 #endif
  1520   return true;
  1523 nsImageMap*
  1524 nsImageFrame::GetImageMap()
  1526   if (!mImageMap) {
  1527     nsIContent* map = GetMapElement();
  1528     if (map) {
  1529       mImageMap = new nsImageMap();
  1530       NS_ADDREF(mImageMap);
  1531       mImageMap->Init(this, map);
  1535   return mImageMap;
  1538 bool
  1539 nsImageFrame::IsServerImageMap()
  1541   return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap);
  1544 // Translate an point that is relative to our frame
  1545 // into a localized pixel coordinate that is relative to the
  1546 // content area of this frame (inside the border+padding).
  1547 void
  1548 nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
  1549                                    nsIntPoint&     aResult)
  1551   nscoord x = aPoint.x;
  1552   nscoord y = aPoint.y;
  1554   // Subtract out border and padding here so that the coordinates are
  1555   // now relative to the content area of this frame.
  1556   nsRect inner = GetInnerArea();
  1557   x -= inner.x;
  1558   y -= inner.y;
  1560   aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
  1561   aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
  1564 bool
  1565 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
  1566                                          nsIContent** aNode)
  1568   bool status = false;
  1569   aTarget.Truncate();
  1570   *aHref = nullptr;
  1571   *aNode = nullptr;
  1573   // Walk up the content tree, looking for an nsIDOMAnchorElement
  1574   for (nsIContent* content = mContent->GetParent();
  1575        content; content = content->GetParent()) {
  1576     nsCOMPtr<dom::Link> link(do_QueryInterface(content));
  1577     if (link) {
  1578       nsCOMPtr<nsIURI> href = content->GetHrefURI();
  1579       if (href) {
  1580         href->Clone(aHref);
  1582       status = (*aHref != nullptr);
  1584       nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
  1585       if (anchor) {
  1586         anchor->GetTarget(aTarget);
  1588       NS_ADDREF(*aNode = content);
  1589       break;
  1592   return status;
  1595 nsresult  
  1596 nsImageFrame::GetContentForEvent(WidgetEvent* aEvent,
  1597                                  nsIContent** aContent)
  1599   NS_ENSURE_ARG_POINTER(aContent);
  1601   nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
  1602   if (f != this) {
  1603     return f->GetContentForEvent(aEvent, aContent);
  1606   // XXX We need to make this special check for area element's capturing the
  1607   // mouse due to bug 135040. Remove it once that's fixed.
  1608   nsIContent* capturingContent =
  1609     aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
  1610                                      nullptr;
  1611   if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
  1612     *aContent = capturingContent;
  1613     NS_IF_ADDREF(*aContent);
  1614     return NS_OK;
  1617   nsImageMap* map = GetImageMap();
  1619   if (nullptr != map) {
  1620     nsIntPoint p;
  1621     TranslateEventCoords(
  1622       nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
  1623     nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
  1624     if (area) {
  1625       area.forget(aContent);
  1626       return NS_OK;
  1630   *aContent = GetContent();
  1631   NS_IF_ADDREF(*aContent);
  1632   return NS_OK;
  1635 // XXX what should clicks on transparent pixels do?
  1636 nsresult
  1637 nsImageFrame::HandleEvent(nsPresContext* aPresContext,
  1638                           WidgetGUIEvent* aEvent,
  1639                           nsEventStatus* aEventStatus)
  1641   NS_ENSURE_ARG_POINTER(aEventStatus);
  1643   if ((aEvent->message == NS_MOUSE_BUTTON_UP && 
  1644        aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
  1645       aEvent->message == NS_MOUSE_MOVE) {
  1646     nsImageMap* map = GetImageMap();
  1647     bool isServerMap = IsServerImageMap();
  1648     if ((nullptr != map) || isServerMap) {
  1649       nsIntPoint p;
  1650       TranslateEventCoords(
  1651         nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
  1652       bool inside = false;
  1653       // Even though client-side image map triggering happens
  1654       // through content, we need to make sure we're not inside
  1655       // (in case we deal with a case of both client-side and
  1656       // sever-side on the same image - it happens!)
  1657       if (nullptr != map) {
  1658         inside = !!map->GetArea(p.x, p.y);
  1661       if (!inside && isServerMap) {
  1663         // Server side image maps use the href in a containing anchor
  1664         // element to provide the basis for the destination url.
  1665         nsCOMPtr<nsIURI> uri;
  1666         nsAutoString target;
  1667         nsCOMPtr<nsIContent> anchorNode;
  1668         if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
  1669                                        getter_AddRefs(anchorNode))) {
  1670           // XXX if the mouse is over/clicked in the border/padding area
  1671           // we should probably just pretend nothing happened. Nav4
  1672           // keeps the x,y coordinates positive as we do; IE doesn't
  1673           // bother. Both of them send the click through even when the
  1674           // mouse is over the border.
  1675           if (p.x < 0) p.x = 0;
  1676           if (p.y < 0) p.y = 0;
  1677           nsAutoCString spec;
  1678           uri->GetSpec(spec);
  1679           spec += nsPrintfCString("?%d,%d", p.x, p.y);
  1680           uri->SetSpec(spec);                
  1682           bool clicked = false;
  1683           if (aEvent->message == NS_MOUSE_BUTTON_UP) {
  1684             *aEventStatus = nsEventStatus_eConsumeDoDefault; 
  1685             clicked = true;
  1687           nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
  1688                                       clicked, true, true);
  1694   return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
  1697 nsresult
  1698 nsImageFrame::GetCursor(const nsPoint& aPoint,
  1699                         nsIFrame::Cursor& aCursor)
  1701   nsImageMap* map = GetImageMap();
  1702   if (nullptr != map) {
  1703     nsIntPoint p;
  1704     TranslateEventCoords(aPoint, p);
  1705     nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
  1706     if (area) {
  1707       // Use the cursor from the style of the *area* element.
  1708       // XXX Using the image as the parent style context isn't
  1709       // technically correct, but it's probably the right thing to do
  1710       // here, since it means that areas on which the cursor isn't
  1711       // specified will inherit the style from the image.
  1712       nsRefPtr<nsStyleContext> areaStyle = 
  1713         PresContext()->PresShell()->StyleSet()->
  1714           ResolveStyleFor(area->AsElement(), StyleContext());
  1715       FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
  1716                                      aCursor);
  1717       if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
  1718         aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
  1720       return NS_OK;
  1723   return nsFrame::GetCursor(aPoint, aCursor);
  1726 nsresult
  1727 nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
  1728                                nsIAtom* aAttribute,
  1729                                int32_t aModType)
  1731   nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID,
  1732                                                     aAttribute, aModType);
  1733   if (NS_FAILED(rv)) {
  1734     return rv;
  1736   if (nsGkAtoms::alt == aAttribute)
  1738     PresContext()->PresShell()->FrameNeedsReflow(this,
  1739                                                  nsIPresShell::eStyleChange,
  1740                                                  NS_FRAME_IS_DIRTY);
  1743   return NS_OK;
  1746 nsIAtom*
  1747 nsImageFrame::GetType() const
  1749   return nsGkAtoms::imageFrame;
  1752 #ifdef DEBUG_FRAME_DUMP
  1753 nsresult
  1754 nsImageFrame::GetFrameName(nsAString& aResult) const
  1756   return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
  1759 void
  1760 nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
  1762   nsCString str;
  1763   ListGeneric(str, aPrefix, aFlags);
  1765   // output the img src url
  1766   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
  1767   if (imageLoader) {
  1768     nsCOMPtr<imgIRequest> currentRequest;
  1769     imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
  1770                             getter_AddRefs(currentRequest));
  1771     if (currentRequest) {
  1772       nsCOMPtr<nsIURI> uri;
  1773       currentRequest->GetURI(getter_AddRefs(uri));
  1774       nsAutoCString uristr;
  1775       uri->GetAsciiSpec(uristr);
  1776       str += nsPrintfCString(" [src=%s]", uristr.get());
  1779   fprintf_stderr(out, "%s\n", str.get());
  1781 #endif
  1783 int
  1784 nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
  1786   int skip = 0;
  1787   if (nullptr != GetPrevInFlow()) {
  1788     skip |= LOGICAL_SIDE_B_START;
  1790   if (nullptr != GetNextInFlow()) {
  1791     skip |= LOGICAL_SIDE_B_END;
  1793   return skip;
  1796 nsresult
  1797 nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
  1799   if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
  1800       mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
  1801     aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(),
  1802                  mIntrinsicSize.height.GetCoordValue());
  1803     return NS_OK;
  1806   return NS_ERROR_FAILURE;
  1809 nsresult
  1810 nsImageFrame::LoadIcon(const nsAString& aSpec,
  1811                        nsPresContext *aPresContext,
  1812                        imgRequestProxy** aRequest)
  1814   nsresult rv = NS_OK;
  1815   NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
  1816   NS_PRECONDITION(aPresContext, "NULL PresContext");
  1818   if (!sIOService) {
  1819     rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
  1820     NS_ENSURE_SUCCESS(rv, rv);
  1823   nsCOMPtr<nsIURI> realURI;
  1824   SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
  1826   nsRefPtr<imgLoader> il =
  1827     nsContentUtils::GetImgLoaderForDocument(aPresContext->Document());
  1829   nsCOMPtr<nsILoadGroup> loadGroup;
  1830   GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
  1832   // For icon loads, we don't need to merge with the loadgroup flags
  1833   nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
  1835   nsCOMPtr<nsIURI> firstPartyIsolationURI;
  1836   nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc
  1837       = do_GetService(THIRDPARTYUTIL_CONTRACTID);
  1838   // XXX: Should we pass the loadgroup, too? Is document ever likely
  1839   // to be unset?
  1840   thirdPartySvc->GetFirstPartyIsolationURI(nullptr, aPresContext->Document(),
  1841                                            getter_AddRefs(firstPartyIsolationURI));
  1843   return il->LoadImage(realURI,                /* icon URI */
  1844                        firstPartyIsolationURI, /* initial document URI; this is only
  1845                                                   relevant for cookies, so does not
  1846                                                   apply to icons. */
  1847                        nullptr,                /* referrer (not relevant for icons) */
  1848                        nullptr,                /* principal (not relevant for icons) */
  1849                        loadGroup,
  1850                        gIconLoad,
  1851                        nullptr,                /* Not associated with any particular document */
  1852                        loadFlags,
  1853                        nullptr,
  1854                        nullptr,                /* channel policy not needed */
  1855                        EmptyString(),
  1856                        aRequest);
  1859 void
  1860 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
  1862   if (mContent) {
  1863     NS_ASSERTION(mContent->GetDocument(),
  1864                  "Frame still alive after content removed from document!");
  1865     aCharset = mContent->GetDocument()->GetDocumentCharacterSet();
  1869 void
  1870 nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
  1871                          nsIURI **aURI)
  1873   nsCOMPtr<nsIURI> baseURI;
  1874   if (mContent) {
  1875     baseURI = mContent->GetBaseURI();
  1877   nsAutoCString charset;
  1878   GetDocumentCharacterSet(charset);
  1879   NS_NewURI(aURI, aSpec, 
  1880             charset.IsEmpty() ? nullptr : charset.get(), 
  1881             baseURI, aIOService);
  1884 void
  1885 nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
  1887   if (!aPresContext)
  1888     return;
  1890   NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
  1892   nsIPresShell *shell = aPresContext->GetPresShell();
  1894   if (!shell)
  1895     return;
  1897   nsIDocument *doc = shell->GetDocument();
  1898   if (!doc)
  1899     return;
  1901   *aLoadGroup = doc->GetDocumentLoadGroup().take();
  1904 nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
  1906   NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
  1908   NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png");
  1909   NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png");
  1911   gIconLoad = new IconLoad();
  1912   NS_ADDREF(gIconLoad);
  1914   nsresult rv;
  1915   // create a loader and load the images
  1916   rv = LoadIcon(loadingSrc,
  1917                 aPresContext,
  1918                 getter_AddRefs(gIconLoad->mLoadingImage));
  1919   if (NS_FAILED(rv)) {
  1920     return rv;
  1923   rv = LoadIcon(brokenSrc,
  1924                 aPresContext,
  1925                 getter_AddRefs(gIconLoad->mBrokenImage));
  1926   return rv;
  1929 NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver,
  1930                   imgINotificationObserver)
  1932 static const char* kIconLoadPrefs[] = {
  1933   "browser.display.force_inline_alttext",
  1934   "browser.display.show_image_placeholders",
  1935   nullptr
  1936 };
  1938 nsImageFrame::IconLoad::IconLoad()
  1940   // register observers
  1941   Preferences::AddStrongObservers(this, kIconLoadPrefs);
  1942   GetPrefs();
  1945 void
  1946 nsImageFrame::IconLoad::Shutdown()
  1948   Preferences::RemoveObservers(this, kIconLoadPrefs);
  1949   // in case the pref service releases us later
  1950   if (mLoadingImage) {
  1951     mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
  1952     mLoadingImage = nullptr;
  1954   if (mBrokenImage) {
  1955     mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
  1956     mBrokenImage = nullptr;
  1960 NS_IMETHODIMP
  1961 nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
  1962                                 const char16_t* aData)
  1964   NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
  1965                "wrong topic");
  1966 #ifdef DEBUG
  1967   // assert |aData| is one of our prefs.
  1968   for (uint32_t i = 0; i < ArrayLength(kIconLoadPrefs) ||
  1969                        (NS_NOTREACHED("wrong pref"), false); ++i)
  1970     if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
  1971       break;
  1972 #endif
  1974   GetPrefs();
  1975   return NS_OK;
  1978 void nsImageFrame::IconLoad::GetPrefs()
  1980   mPrefForceInlineAltText =
  1981     Preferences::GetBool("browser.display.force_inline_alttext");
  1983   mPrefShowPlaceholders =
  1984     Preferences::GetBool("browser.display.show_image_placeholders", true);
  1987 NS_IMETHODIMP
  1988 nsImageFrame::IconLoad::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
  1990   if (aType != imgINotificationObserver::LOAD_COMPLETE &&
  1991       aType != imgINotificationObserver::FRAME_UPDATE) {
  1992     return NS_OK;
  1995   nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
  1996   nsImageFrame *frame;
  1997   while (iter.HasMore()) {
  1998     frame = iter.GetNext();
  1999     frame->InvalidateFrame();
  2002   return NS_OK;
  2005 NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
  2007 nsImageListener::nsImageListener(nsImageFrame *aFrame) :
  2008   mFrame(aFrame)
  2012 nsImageListener::~nsImageListener()
  2016 NS_IMETHODIMP
  2017 nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
  2019   if (!mFrame)
  2020     return NS_ERROR_FAILURE;
  2022   return mFrame->Notify(aRequest, aType, aData);
  2025 static bool
  2026 IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
  2028   if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
  2029     return false;
  2030   // Check if the parent of the closest nsBlockFrame has auto width.
  2031   nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
  2032   if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
  2033     // Assume direct parent is a table cell frame.
  2034     nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
  2035     return grandAncestor &&
  2036       grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
  2038   return false;
  2041 /* virtual */ void
  2042 nsImageFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
  2043                                 nsIFrame::InlineMinWidthData *aData)
  2046   NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
  2048   nsIFrame* parent = GetParent();
  2049   bool canBreak =
  2050     !CanContinueTextRun() &&
  2051     parent->StyleText()->WhiteSpaceCanWrap(parent) &&
  2052     !IsInAutoWidthTableCellForQuirk(this);
  2054   if (canBreak)
  2055     aData->OptionallyBreak(aRenderingContext);
  2057   aData->trailingWhitespace = 0;
  2058   aData->skipWhitespace = false;
  2059   aData->trailingTextFrame = nullptr;
  2060   aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
  2061                             this, nsLayoutUtils::MIN_WIDTH);
  2062   aData->atStartOfLine = false;
  2064   if (canBreak)
  2065     aData->OptionallyBreak(aRenderingContext);

mercurial