layout/generic/nsImageFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/generic/nsImageFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2067 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/* rendering object for replaced elements with bitmap image data */
    1.10 +
    1.11 +#include "nsImageFrame.h"
    1.12 +
    1.13 +#include "mozilla/DebugOnly.h"
    1.14 +#include "mozilla/EventStates.h"
    1.15 +#include "mozilla/MouseEvents.h"
    1.16 +
    1.17 +#include "nsCOMPtr.h"
    1.18 +#include "nsIImageLoadingContent.h"
    1.19 +#include "nsString.h"
    1.20 +#include "nsPrintfCString.h"
    1.21 +#include "nsPresContext.h"
    1.22 +#include "nsRenderingContext.h"
    1.23 +#include "nsIPresShell.h"
    1.24 +#include "nsGkAtoms.h"
    1.25 +#include "nsIDocument.h"
    1.26 +#include "nsContentUtils.h"
    1.27 +#include "nsCSSAnonBoxes.h"
    1.28 +#include "nsStyleContext.h"
    1.29 +#include "nsStyleConsts.h"
    1.30 +#include "nsStyleCoord.h"
    1.31 +#include "nsTransform2D.h"
    1.32 +#include "nsImageMap.h"
    1.33 +#include "nsIIOService.h"
    1.34 +#include "nsILoadGroup.h"
    1.35 +#include "nsISupportsPriority.h"
    1.36 +#include "nsNetUtil.h"
    1.37 +#include "nsCSSRendering.h"
    1.38 +#include "nsIDOMHTMLAnchorElement.h"
    1.39 +#include "nsNameSpaceManager.h"
    1.40 +#include <algorithm>
    1.41 +#ifdef ACCESSIBILITY
    1.42 +#include "nsAccessibilityService.h"
    1.43 +#endif
    1.44 +#include "nsIDOMNode.h"
    1.45 +#include "nsLayoutUtils.h"
    1.46 +#include "nsDisplayList.h"
    1.47 +
    1.48 +#include "imgIContainer.h"
    1.49 +#include "imgLoader.h"
    1.50 +#include "imgRequestProxy.h"
    1.51 +
    1.52 +#include "nsCSSFrameConstructor.h"
    1.53 +#include "nsIDOMRange.h"
    1.54 +
    1.55 +#include "nsError.h"
    1.56 +#include "nsBidiUtils.h"
    1.57 +#include "nsBidiPresUtils.h"
    1.58 +#include "mozIThirdPartyUtil.h"
    1.59 +
    1.60 +#include "gfxRect.h"
    1.61 +#include "ImageLayers.h"
    1.62 +#include "ImageContainer.h"
    1.63 +#include "nsStyleSet.h"
    1.64 +#include "nsBlockFrame.h"
    1.65 +#include "nsStyleStructInlines.h"
    1.66 +
    1.67 +#include "mozilla/Preferences.h"
    1.68 +
    1.69 +#include "mozilla/dom/Link.h"
    1.70 +
    1.71 +using namespace mozilla;
    1.72 +
    1.73 +// sizes (pixels) for image icon, padding and border frame
    1.74 +#define ICON_SIZE        (16)
    1.75 +#define ICON_PADDING     (3)
    1.76 +#define ALT_BORDER_WIDTH (1)
    1.77 +
    1.78 +
    1.79 +//we must add hooks soon
    1.80 +#define IMAGE_EDITOR_CHECK 1
    1.81 +
    1.82 +// Default alignment value (so we can tell an unset value from a set value)
    1.83 +#define ALIGN_UNSET uint8_t(-1)
    1.84 +
    1.85 +using namespace mozilla::layers;
    1.86 +using namespace mozilla::dom;
    1.87 +
    1.88 +// static icon information
    1.89 +nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr;
    1.90 +
    1.91 +// cached IO service for loading icons
    1.92 +nsIIOService* nsImageFrame::sIOService;
    1.93 +
    1.94 +// test if the width and height are fixed, looking at the style data
    1.95 +static bool HaveFixedSize(const nsStylePosition* aStylePosition)
    1.96 +{
    1.97 +  // check the width and height values in the reflow state's style struct
    1.98 +  // - if width and height are specified as either coord or percentage, then
    1.99 +  //   the size of the image frame is constrained
   1.100 +  return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
   1.101 +         aStylePosition->mHeight.IsCoordPercentCalcUnit();
   1.102 +}
   1.103 +// use the data in the reflow state to decide if the image has a constrained size
   1.104 +// (i.e. width and height that are based on the containing block size and not the image size) 
   1.105 +// so we can avoid animated GIF related reflows
   1.106 +inline bool HaveFixedSize(const nsHTMLReflowState& aReflowState)
   1.107 +{ 
   1.108 +  NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition");
   1.109 +  // when an image has percent css style height or width, but ComputedHeight() 
   1.110 +  // or ComputedWidth() of reflow state is  NS_UNCONSTRAINEDSIZE  
   1.111 +  // it needs to return false to cause an incremental reflow later
   1.112 +  // if an image is inside table like bug 156731 simple testcase III, 
   1.113 +  // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE
   1.114 +  // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false
   1.115 +  // see bug 156731
   1.116 +  const nsStyleCoord &height = aReflowState.mStylePosition->mHeight;
   1.117 +  const nsStyleCoord &width = aReflowState.mStylePosition->mWidth;
   1.118 +  return ((height.HasPercent() &&
   1.119 +           NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) ||
   1.120 +          (width.HasPercent() &&
   1.121 +           (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() ||
   1.122 +            0 == aReflowState.ComputedWidth())))
   1.123 +          ? false
   1.124 +          : HaveFixedSize(aReflowState.mStylePosition); 
   1.125 +}
   1.126 +
   1.127 +nsIFrame*
   1.128 +NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.129 +{
   1.130 +  return new (aPresShell) nsImageFrame(aContext);
   1.131 +}
   1.132 +
   1.133 +NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
   1.134 +
   1.135 +
   1.136 +nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
   1.137 +  ImageFrameSuper(aContext),
   1.138 +  mComputedSize(0, 0),
   1.139 +  mIntrinsicRatio(0, 0),
   1.140 +  mDisplayingIcon(false),
   1.141 +  mFirstFrameComplete(false),
   1.142 +  mReflowCallbackPosted(false)
   1.143 +{
   1.144 +  // We assume our size is not constrained and we haven't gotten an
   1.145 +  // initial reflow yet, so don't touch those flags.
   1.146 +  mIntrinsicSize.width.SetCoordValue(0);
   1.147 +  mIntrinsicSize.height.SetCoordValue(0);
   1.148 +}
   1.149 +
   1.150 +nsImageFrame::~nsImageFrame()
   1.151 +{
   1.152 +}
   1.153 +
   1.154 +NS_QUERYFRAME_HEAD(nsImageFrame)
   1.155 +  NS_QUERYFRAME_ENTRY(nsImageFrame)
   1.156 +NS_QUERYFRAME_TAIL_INHERITING(ImageFrameSuper)
   1.157 +
   1.158 +#ifdef ACCESSIBILITY
   1.159 +a11y::AccType
   1.160 +nsImageFrame::AccessibleType()
   1.161 +{
   1.162 +  // Don't use GetImageMap() to avoid reentrancy into accessibility.
   1.163 +  if (HasImageMap()) {
   1.164 +    return a11y::eHTMLImageMapType;
   1.165 +  }
   1.166 +
   1.167 +  return a11y::eImageType;
   1.168 +}
   1.169 +#endif
   1.170 +
   1.171 +void
   1.172 +nsImageFrame::DisconnectMap()
   1.173 +{
   1.174 +  if (mImageMap) {
   1.175 +    mImageMap->Destroy();
   1.176 +    NS_RELEASE(mImageMap);
   1.177 +
   1.178 +#ifdef ACCESSIBILITY
   1.179 +  nsAccessibilityService* accService = GetAccService();
   1.180 +  if (accService) {
   1.181 +    accService->RecreateAccessible(PresContext()->PresShell(), mContent);
   1.182 +  }
   1.183 +#endif
   1.184 +  }
   1.185 +}
   1.186 +
   1.187 +void
   1.188 +nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
   1.189 +{
   1.190 +  if (mReflowCallbackPosted) {
   1.191 +    PresContext()->PresShell()->CancelReflowCallback(this);
   1.192 +    mReflowCallbackPosted = false;
   1.193 +  }
   1.194 +
   1.195 +  // Tell our image map, if there is one, to clean up
   1.196 +  // This causes the nsImageMap to unregister itself as
   1.197 +  // a DOM listener.
   1.198 +  DisconnectMap();
   1.199 +
   1.200 +  // set the frame to null so we don't send messages to a dead object.
   1.201 +  if (mListener) {
   1.202 +    nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   1.203 +    if (imageLoader) {
   1.204 +      // Notify our image loading content that we are going away so it can
   1.205 +      // deregister with our refresh driver.
   1.206 +      imageLoader->FrameDestroyed(this);
   1.207 +
   1.208 +      imageLoader->RemoveObserver(mListener);
   1.209 +    }
   1.210 +    
   1.211 +    reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr);
   1.212 +  }
   1.213 +  
   1.214 +  mListener = nullptr;
   1.215 +
   1.216 +  // If we were displaying an icon, take ourselves off the list
   1.217 +  if (mDisplayingIcon)
   1.218 +    gIconLoad->RemoveIconObserver(this);
   1.219 +
   1.220 +  nsSplittableFrame::DestroyFrom(aDestructRoot);
   1.221 +}
   1.222 +
   1.223 +
   1.224 +
   1.225 +void
   1.226 +nsImageFrame::Init(nsIContent*      aContent,
   1.227 +                   nsIFrame*        aParent,
   1.228 +                   nsIFrame*        aPrevInFlow)
   1.229 +{
   1.230 +  nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
   1.231 +
   1.232 +  mListener = new nsImageListener(this);
   1.233 +
   1.234 +  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
   1.235 +  if (!imageLoader) {
   1.236 +    NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
   1.237 +  }
   1.238 +
   1.239 +  imageLoader->AddObserver(mListener);
   1.240 +
   1.241 +  nsPresContext *aPresContext = PresContext();
   1.242 +  
   1.243 +  if (!gIconLoad)
   1.244 +    LoadIcons(aPresContext);
   1.245 +
   1.246 +  // We have a PresContext now, so we need to notify the image content node
   1.247 +  // that it can register images.
   1.248 +  imageLoader->FrameCreated(this);
   1.249 +
   1.250 +  // Give image loads associated with an image frame a small priority boost!
   1.251 +  nsCOMPtr<imgIRequest> currentRequest;
   1.252 +  imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   1.253 +                          getter_AddRefs(currentRequest));
   1.254 +  nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
   1.255 +  if (p)
   1.256 +    p->AdjustPriority(-1);
   1.257 +
   1.258 +  // If we already have an image container, OnStartContainer won't be called
   1.259 +  if (currentRequest) {
   1.260 +    nsCOMPtr<imgIContainer> image;
   1.261 +    currentRequest->GetImage(getter_AddRefs(image));
   1.262 +    OnStartContainer(currentRequest, image);
   1.263 +  }
   1.264 +}
   1.265 +
   1.266 +bool
   1.267 +nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
   1.268 +{
   1.269 +  NS_PRECONDITION(aImage, "null image");
   1.270 +  if (!aImage)
   1.271 +    return false;
   1.272 +
   1.273 +  IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
   1.274 +  mIntrinsicSize = IntrinsicSize();
   1.275 +
   1.276 +  // Set intrinsic size to match aImage's reported intrinsic width & height.
   1.277 +  nsSize intrinsicSize;
   1.278 +  if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
   1.279 +    // If the image has no intrinsic width, intrinsicSize.width will be -1, and
   1.280 +    // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
   1.281 +    // Otherwise we use intrinsicSize.width. Height works the same way.
   1.282 +    if (intrinsicSize.width != -1)
   1.283 +      mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
   1.284 +    if (intrinsicSize.height != -1)
   1.285 +      mIntrinsicSize.height.SetCoordValue(intrinsicSize.height);
   1.286 +  } else {
   1.287 +    // Failure means that the image hasn't loaded enough to report a result. We
   1.288 +    // treat this case as if the image's intrinsic size was 0x0.
   1.289 +    mIntrinsicSize.width.SetCoordValue(0);
   1.290 +    mIntrinsicSize.height.SetCoordValue(0);
   1.291 +  }
   1.292 +
   1.293 +  return mIntrinsicSize != oldIntrinsicSize;
   1.294 +}
   1.295 +
   1.296 +bool
   1.297 +nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
   1.298 +{
   1.299 +  NS_PRECONDITION(aImage, "null image");
   1.300 +
   1.301 +  if (!aImage)
   1.302 +    return false;
   1.303 +
   1.304 +  nsSize oldIntrinsicRatio = mIntrinsicRatio;
   1.305 +
   1.306 +  // Set intrinsic ratio to match aImage's reported intrinsic ratio.
   1.307 +  if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
   1.308 +    mIntrinsicRatio.SizeTo(0, 0);
   1.309 +
   1.310 +  return mIntrinsicRatio != oldIntrinsicRatio;
   1.311 +}
   1.312 +
   1.313 +bool
   1.314 +nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
   1.315 +{
   1.316 +  // Set the translation components.
   1.317 +  // XXXbz does this introduce rounding errors because of the cast to
   1.318 +  // float?  Should we just manually add that stuff in every time
   1.319 +  // instead?
   1.320 +  nsRect innerArea = GetInnerArea();
   1.321 +  aTransform.SetToTranslate(float(innerArea.x),
   1.322 +                            float(innerArea.y - GetContinuationOffset()));
   1.323 +
   1.324 +  // Set the scale factors.
   1.325 +  if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
   1.326 +      mIntrinsicSize.width.GetCoordValue() != 0 &&
   1.327 +      mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
   1.328 +      mIntrinsicSize.height.GetCoordValue() != 0 &&
   1.329 +      mIntrinsicSize.width.GetCoordValue() != mComputedSize.width &&
   1.330 +      mIntrinsicSize.height.GetCoordValue() != mComputedSize.height) {
   1.331 +
   1.332 +    aTransform.SetScale(float(mComputedSize.width)  /
   1.333 +                        float(mIntrinsicSize.width.GetCoordValue()),
   1.334 +                        float(mComputedSize.height) /
   1.335 +                        float(mIntrinsicSize.height.GetCoordValue()));
   1.336 +    return true;
   1.337 +  }
   1.338 +
   1.339 +  return false;
   1.340 +}
   1.341 +
   1.342 +/*
   1.343 + * These two functions basically do the same check.  The first one
   1.344 + * checks that the given request is the current request for our
   1.345 + * mContent.  The second checks that the given image container the
   1.346 + * same as the image container on the current request for our
   1.347 + * mContent.
   1.348 + */
   1.349 +bool
   1.350 +nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
   1.351 +{
   1.352 +  // Default to pending load in case of errors
   1.353 +  nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
   1.354 +  NS_ASSERTION(imageLoader, "No image loading content?");
   1.355 +
   1.356 +  int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
   1.357 +  imageLoader->GetRequestType(aRequest, &requestType);
   1.358 +
   1.359 +  return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
   1.360 +}
   1.361 +
   1.362 +bool
   1.363 +nsImageFrame::IsPendingLoad(imgIContainer* aContainer) const
   1.364 +{
   1.365 +  //  default to pending load in case of errors
   1.366 +  if (!aContainer) {
   1.367 +    NS_ERROR("No image container!");
   1.368 +    return true;
   1.369 +  }
   1.370 +
   1.371 +  nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
   1.372 +  NS_ASSERTION(imageLoader, "No image loading content?");
   1.373 +  
   1.374 +  nsCOMPtr<imgIRequest> currentRequest;
   1.375 +  imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   1.376 +                          getter_AddRefs(currentRequest));
   1.377 +  if (!currentRequest) {
   1.378 +    NS_ERROR("No current request");
   1.379 +    return true;
   1.380 +  }
   1.381 +
   1.382 +  nsCOMPtr<imgIContainer> currentContainer;
   1.383 +  currentRequest->GetImage(getter_AddRefs(currentContainer));
   1.384 +
   1.385 +  return currentContainer != aContainer;
   1.386 +  
   1.387 +}
   1.388 +
   1.389 +nsRect
   1.390 +nsImageFrame::SourceRectToDest(const nsIntRect& aRect)
   1.391 +{
   1.392 +  // When scaling the image, row N of the source image may (depending on
   1.393 +  // the scaling function) be used to draw any row in the destination image
   1.394 +  // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
   1.395 +  // floating-point scaling factor.  The same holds true for columns.
   1.396 +  // So, we start by computing that bound without the floor and ceiling.
   1.397 +
   1.398 +  nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
   1.399 +           nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
   1.400 +           nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
   1.401 +           nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
   1.402 +
   1.403 +  nsTransform2D sourceToDest;
   1.404 +  if (!GetSourceToDestTransform(sourceToDest)) {
   1.405 +    // Failed to generate transform matrix. Return our whole inner area,
   1.406 +    // to be on the safe side (since this method is used for generating
   1.407 +    // invalidation rects).
   1.408 +    return GetInnerArea();
   1.409 +  }
   1.410 +
   1.411 +  sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
   1.412 +
   1.413 +  // Now, round the edges out to the pixel boundary.
   1.414 +  nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
   1.415 +  nscoord right = r.x + r.width;
   1.416 +  nscoord bottom = r.y + r.height;
   1.417 +
   1.418 +  r.x -= (scale + (r.x % scale)) % scale;
   1.419 +  r.y -= (scale + (r.y % scale)) % scale;
   1.420 +  r.width = right + ((scale - (right % scale)) % scale) - r.x;
   1.421 +  r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
   1.422 +
   1.423 +  return r;
   1.424 +}
   1.425 +
   1.426 +// Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK".  This means
   1.427 +// that we'll construct image frames for them as needed if their display is
   1.428 +// toggled from "none" (though we won't paint them, unless their visibility
   1.429 +// is changed too).
   1.430 +#define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
   1.431 +                    NS_EVENT_STATE_LOADING)
   1.432 +
   1.433 +// This is a macro so that we don't evaluate the boolean last arg
   1.434 +// unless we have to; it can be expensive
   1.435 +#define IMAGE_OK(_state, _loadingOK)                                           \
   1.436 +   (!(_state).HasAtLeastOneOfStates(BAD_STATES) ||                                    \
   1.437 +    (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
   1.438 +     (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
   1.439 +
   1.440 +/* static */
   1.441 +bool
   1.442 +nsImageFrame::ShouldCreateImageFrameFor(Element* aElement,
   1.443 +                                        nsStyleContext* aStyleContext)
   1.444 +{
   1.445 +  EventStates state = aElement->State();
   1.446 +  if (IMAGE_OK(state,
   1.447 +               HaveFixedSize(aStyleContext->StylePosition()))) {
   1.448 +    // Image is fine; do the image frame thing
   1.449 +    return true;
   1.450 +  }
   1.451 +
   1.452 +  // Check if we want to use a placeholder box with an icon or just
   1.453 +  // let the presShell make us into inline text.  Decide as follows:
   1.454 +  //
   1.455 +  //  - if our special "force icons" style is set, show an icon
   1.456 +  //  - else if our "do not show placeholders" pref is set, skip the icon
   1.457 +  //  - else:
   1.458 +  //  - if there is a src attribute, there is no alt attribute,
   1.459 +  //    and this is not an <object> (which could not possibly have
   1.460 +  //    such an attribute), show an icon.
   1.461 +  //  - if QuirksMode, and the IMG has a size show an icon.
   1.462 +  //  - otherwise, skip the icon
   1.463 +  bool useSizedBox;
   1.464 +  
   1.465 +  if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) {
   1.466 +    useSizedBox = true;
   1.467 +  }
   1.468 +  else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
   1.469 +    useSizedBox = false;
   1.470 +  }
   1.471 +  else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
   1.472 +           !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
   1.473 +           !aElement->IsHTML(nsGkAtoms::object) &&
   1.474 +           !aElement->IsHTML(nsGkAtoms::input)) {
   1.475 +    // Use a sized box if we have no alt text.  This means no alt attribute
   1.476 +    // and the node is not an object or an input (since those always have alt
   1.477 +    // text).
   1.478 +    useSizedBox = true;
   1.479 +  }
   1.480 +  else if (aStyleContext->PresContext()->CompatibilityMode() !=
   1.481 +           eCompatibility_NavQuirks) {
   1.482 +    useSizedBox = false;
   1.483 +  }
   1.484 +  else {
   1.485 +    // check whether we have fixed size
   1.486 +    useSizedBox = HaveFixedSize(aStyleContext->StylePosition());
   1.487 +  }
   1.488 +
   1.489 +  return useSizedBox;
   1.490 +}
   1.491 +
   1.492 +nsresult
   1.493 +nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
   1.494 +{
   1.495 +  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
   1.496 +    nsCOMPtr<imgIContainer> image;
   1.497 +    aRequest->GetImage(getter_AddRefs(image));
   1.498 +    return OnStartContainer(aRequest, image);
   1.499 +  }
   1.500 +
   1.501 +  if (aType == imgINotificationObserver::FRAME_UPDATE) {
   1.502 +    return OnDataAvailable(aRequest, aData);
   1.503 +  }
   1.504 +
   1.505 +  if (aType == imgINotificationObserver::FRAME_COMPLETE) {
   1.506 +    mFirstFrameComplete = true;
   1.507 +  }
   1.508 +
   1.509 +  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
   1.510 +    uint32_t imgStatus;
   1.511 +    aRequest->GetImageStatus(&imgStatus);
   1.512 +    nsresult status =
   1.513 +        imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
   1.514 +    return OnStopRequest(aRequest, status);
   1.515 +  }
   1.516 +
   1.517 +  return NS_OK;
   1.518 +}
   1.519 +
   1.520 +static bool
   1.521 +SizeIsAvailable(imgIRequest* aRequest)
   1.522 +{
   1.523 +  if (!aRequest)
   1.524 +    return false;
   1.525 +
   1.526 +  uint32_t imageStatus = 0;
   1.527 +  nsresult rv = aRequest->GetImageStatus(&imageStatus);
   1.528 +
   1.529 +  return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE); 
   1.530 +}
   1.531 +
   1.532 +nsresult
   1.533 +nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage)
   1.534 +{
   1.535 +  if (!aImage) return NS_ERROR_INVALID_ARG;
   1.536 +
   1.537 +  /* Get requested animation policy from the pres context:
   1.538 +   *   normal = 0
   1.539 +   *   one frame = 1
   1.540 +   *   one loop = 2
   1.541 +   */
   1.542 +  nsPresContext *presContext = PresContext();
   1.543 +  aImage->SetAnimationMode(presContext->ImageAnimationMode());
   1.544 +
   1.545 +  if (IsPendingLoad(aRequest)) {
   1.546 +    // We don't care
   1.547 +    return NS_OK;
   1.548 +  }
   1.549 +
   1.550 +  bool intrinsicSizeChanged = false;
   1.551 +  if (SizeIsAvailable(aRequest)) {
   1.552 +    // This is valid and for the current request, so update our stored image
   1.553 +    // container, orienting according to our style.
   1.554 +    mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
   1.555 +    
   1.556 +    intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
   1.557 +    intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
   1.558 +  } else {
   1.559 +    // We no longer have a valid image, so release our stored image container.
   1.560 +    mImage = nullptr;
   1.561 +
   1.562 +    // Have to size to 0,0 so that GetDesiredSize recalculates the size.
   1.563 +    mIntrinsicSize.width.SetCoordValue(0);
   1.564 +    mIntrinsicSize.height.SetCoordValue(0);
   1.565 +    mIntrinsicRatio.SizeTo(0, 0);
   1.566 +    intrinsicSizeChanged = true;
   1.567 +  }
   1.568 +
   1.569 +  if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) {
   1.570 +    // Now we need to reflow if we have an unconstrained size and have
   1.571 +    // already gotten the initial reflow
   1.572 +    if (!(mState & IMAGE_SIZECONSTRAINED)) { 
   1.573 +      nsIPresShell *presShell = presContext->GetPresShell();
   1.574 +      NS_ASSERTION(presShell, "No PresShell.");
   1.575 +      if (presShell) { 
   1.576 +        presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
   1.577 +                                    NS_FRAME_IS_DIRTY);
   1.578 +      }
   1.579 +    }
   1.580 +  }
   1.581 +
   1.582 +  return NS_OK;
   1.583 +}
   1.584 +
   1.585 +nsresult
   1.586 +nsImageFrame::OnDataAvailable(imgIRequest *aRequest,
   1.587 +                              const nsIntRect *aRect)
   1.588 +{
   1.589 +  if (mFirstFrameComplete) {
   1.590 +    nsCOMPtr<imgIContainer> container;
   1.591 +    aRequest->GetImage(getter_AddRefs(container));
   1.592 +    return FrameChanged(aRequest, container);
   1.593 +  }
   1.594 +
   1.595 +  // XXX do we need to make sure that the reflow from the
   1.596 +  // OnStartContainer has been processed before we start calling
   1.597 +  // invalidate?
   1.598 +
   1.599 +  NS_ENSURE_ARG_POINTER(aRect);
   1.600 +
   1.601 +  if (!(mState & IMAGE_GOTINITIALREFLOW)) {
   1.602 +    // Don't bother to do anything; we have a reflow coming up!
   1.603 +    return NS_OK;
   1.604 +  }
   1.605 +  
   1.606 +  if (IsPendingLoad(aRequest)) {
   1.607 +    // We don't care
   1.608 +    return NS_OK;
   1.609 +  }
   1.610 +
   1.611 +#ifdef DEBUG_decode
   1.612 +  printf("Source rect (%d,%d,%d,%d)\n",
   1.613 +         aRect->x, aRect->y, aRect->width, aRect->height);
   1.614 +#endif
   1.615 +
   1.616 +  if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
   1.617 +    InvalidateFrame(nsDisplayItem::TYPE_IMAGE);
   1.618 +    InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK);
   1.619 +  } else {
   1.620 +    nsRect invalid = SourceRectToDest(*aRect);
   1.621 +    InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE);
   1.622 +    InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK);
   1.623 +  }
   1.624 +
   1.625 +  return NS_OK;
   1.626 +}
   1.627 +
   1.628 +nsresult
   1.629 +nsImageFrame::OnStopRequest(imgIRequest *aRequest,
   1.630 +                            nsresult aStatus)
   1.631 +{
   1.632 +  // Check what request type we're dealing with
   1.633 +  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   1.634 +  NS_ASSERTION(imageLoader, "Who's notifying us??");
   1.635 +  int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
   1.636 +  imageLoader->GetRequestType(aRequest, &loadType);
   1.637 +  if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
   1.638 +      loadType != nsIImageLoadingContent::PENDING_REQUEST) {
   1.639 +    return NS_ERROR_FAILURE;
   1.640 +  }
   1.641 +
   1.642 +  NotifyNewCurrentRequest(aRequest, aStatus);
   1.643 +  return NS_OK;
   1.644 +}
   1.645 +
   1.646 +void
   1.647 +nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
   1.648 +                                      nsresult aStatus)
   1.649 +{
   1.650 +  nsCOMPtr<imgIContainer> image;
   1.651 +  aRequest->GetImage(getter_AddRefs(image));
   1.652 +  NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
   1.653 +
   1.654 +  // May have to switch sizes here!
   1.655 +  bool intrinsicSizeChanged = true;
   1.656 +  if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
   1.657 +    // Update our stored image container, orienting according to our style.
   1.658 +    mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
   1.659 +
   1.660 +    intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
   1.661 +    intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
   1.662 +  } else {
   1.663 +    // We no longer have a valid image, so release our stored image container.
   1.664 +    mImage = nullptr;
   1.665 +
   1.666 +    // Have to size to 0,0 so that GetDesiredSize recalculates the size
   1.667 +    mIntrinsicSize.width.SetCoordValue(0);
   1.668 +    mIntrinsicSize.height.SetCoordValue(0);
   1.669 +    mIntrinsicRatio.SizeTo(0, 0);
   1.670 +  }
   1.671 +
   1.672 +  if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet
   1.673 +    if (!(mState & IMAGE_SIZECONSTRAINED) && intrinsicSizeChanged) {
   1.674 +      nsIPresShell *presShell = PresContext()->GetPresShell();
   1.675 +      if (presShell) { 
   1.676 +        presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
   1.677 +                                    NS_FRAME_IS_DIRTY);
   1.678 +      }
   1.679 +    }
   1.680 +    // Update border+content to account for image change
   1.681 +    InvalidateFrame();
   1.682 +  }
   1.683 +}
   1.684 +
   1.685 +nsresult
   1.686 +nsImageFrame::FrameChanged(imgIRequest *aRequest,
   1.687 +                           imgIContainer *aContainer)
   1.688 +{
   1.689 +  if (!StyleVisibility()->IsVisible()) {
   1.690 +    return NS_OK;
   1.691 +  }
   1.692 +
   1.693 +  if (IsPendingLoad(aContainer)) {
   1.694 +    // We don't care about it
   1.695 +    return NS_OK;
   1.696 +  }
   1.697 +
   1.698 +  InvalidateLayer(nsDisplayItem::TYPE_IMAGE);
   1.699 +  return NS_OK;
   1.700 +}
   1.701 +
   1.702 +void
   1.703 +nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext)
   1.704 +{
   1.705 +  // If mIntrinsicSize.width and height are 0, then we need to update from the
   1.706 +  // image container.
   1.707 +  if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
   1.708 +      mIntrinsicSize.width.GetCoordValue() == 0 &&
   1.709 +      mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
   1.710 +      mIntrinsicSize.height.GetCoordValue() == 0) {
   1.711 +
   1.712 +    if (mImage) {
   1.713 +      UpdateIntrinsicSize(mImage);
   1.714 +      UpdateIntrinsicRatio(mImage);
   1.715 +    } else {
   1.716 +      // image request is null or image size not known, probably an
   1.717 +      // invalid image specified
   1.718 +      // - make the image big enough for the icon (it may not be
   1.719 +      // used if inline alt expansion is used instead)
   1.720 +      if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
   1.721 +        nscoord edgeLengthToUse =
   1.722 +          nsPresContext::CSSPixelsToAppUnits(
   1.723 +            ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
   1.724 +        mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
   1.725 +        mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
   1.726 +        mIntrinsicRatio.SizeTo(1, 1);
   1.727 +      }
   1.728 +    }
   1.729 +  }
   1.730 +}
   1.731 +
   1.732 +/* virtual */ nsSize
   1.733 +nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext,
   1.734 +                          nsSize aCBSize, nscoord aAvailableWidth,
   1.735 +                          nsSize aMargin, nsSize aBorder, nsSize aPadding,
   1.736 +                          uint32_t aFlags)
   1.737 +{
   1.738 +  nsPresContext *presContext = PresContext();
   1.739 +  EnsureIntrinsicSizeAndRatio(presContext);
   1.740 +
   1.741 +  return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
   1.742 +                            aRenderingContext, this,
   1.743 +                            mIntrinsicSize, mIntrinsicRatio, aCBSize,
   1.744 +                            aMargin, aBorder, aPadding);
   1.745 +}
   1.746 +
   1.747 +nsRect 
   1.748 +nsImageFrame::GetInnerArea() const
   1.749 +{
   1.750 +  return GetContentRect() - GetPosition();
   1.751 +}
   1.752 +
   1.753 +// get the offset into the content area of the image where aImg starts if it is a continuation.
   1.754 +nscoord 
   1.755 +nsImageFrame::GetContinuationOffset() const
   1.756 +{
   1.757 +  nscoord offset = 0;
   1.758 +  for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
   1.759 +    offset += f->GetContentRect().height;
   1.760 +  }
   1.761 +  NS_ASSERTION(offset >= 0, "bogus GetContentRect");
   1.762 +  return offset;
   1.763 +}
   1.764 +
   1.765 +/* virtual */ nscoord
   1.766 +nsImageFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
   1.767 +{
   1.768 +  // XXX The caller doesn't account for constraints of the height,
   1.769 +  // min-height, and max-height properties.
   1.770 +  DebugOnly<nscoord> result;
   1.771 +  DISPLAY_MIN_WIDTH(this, result);
   1.772 +  nsPresContext *presContext = PresContext();
   1.773 +  EnsureIntrinsicSizeAndRatio(presContext);
   1.774 +  return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
   1.775 +    mIntrinsicSize.width.GetCoordValue() : 0;
   1.776 +}
   1.777 +
   1.778 +/* virtual */ nscoord
   1.779 +nsImageFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
   1.780 +{
   1.781 +  // XXX The caller doesn't account for constraints of the height,
   1.782 +  // min-height, and max-height properties.
   1.783 +  DebugOnly<nscoord> result;
   1.784 +  DISPLAY_PREF_WIDTH(this, result);
   1.785 +  nsPresContext *presContext = PresContext();
   1.786 +  EnsureIntrinsicSizeAndRatio(presContext);
   1.787 +  // convert from normal twips to scaled twips (printing...)
   1.788 +  return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
   1.789 +    mIntrinsicSize.width.GetCoordValue() : 0;
   1.790 +}
   1.791 +
   1.792 +/* virtual */ IntrinsicSize
   1.793 +nsImageFrame::GetIntrinsicSize()
   1.794 +{
   1.795 +  return mIntrinsicSize;
   1.796 +}
   1.797 +
   1.798 +/* virtual */ nsSize
   1.799 +nsImageFrame::GetIntrinsicRatio()
   1.800 +{
   1.801 +  return mIntrinsicRatio;
   1.802 +}
   1.803 +
   1.804 +nsresult
   1.805 +nsImageFrame::Reflow(nsPresContext*          aPresContext,
   1.806 +                     nsHTMLReflowMetrics&     aMetrics,
   1.807 +                     const nsHTMLReflowState& aReflowState,
   1.808 +                     nsReflowStatus&          aStatus)
   1.809 +{
   1.810 +  DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
   1.811 +  DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   1.812 +  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
   1.813 +                  ("enter nsImageFrame::Reflow: availSize=%d,%d",
   1.814 +                  aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
   1.815 +
   1.816 +  NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
   1.817 +
   1.818 +  aStatus = NS_FRAME_COMPLETE;
   1.819 +
   1.820 +  // see if we have a frozen size (i.e. a fixed width and height)
   1.821 +  if (HaveFixedSize(aReflowState)) {
   1.822 +    mState |= IMAGE_SIZECONSTRAINED;
   1.823 +  } else {
   1.824 +    mState &= ~IMAGE_SIZECONSTRAINED;
   1.825 +  }
   1.826 +
   1.827 +  // XXXldb These two bits are almost exact opposites (except in the
   1.828 +  // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
   1.829 +  if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
   1.830 +    mState |= IMAGE_GOTINITIALREFLOW;
   1.831 +  }
   1.832 +
   1.833 +  mComputedSize = 
   1.834 +    nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
   1.835 +
   1.836 +  aMetrics.Width() = mComputedSize.width;
   1.837 +  aMetrics.Height() = mComputedSize.height;
   1.838 +
   1.839 +  // add borders and padding
   1.840 +  aMetrics.Width()  += aReflowState.ComputedPhysicalBorderPadding().LeftRight();
   1.841 +  aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().TopBottom();
   1.842 +  
   1.843 +  if (GetPrevInFlow()) {
   1.844 +    aMetrics.Width() = GetPrevInFlow()->GetSize().width;
   1.845 +    nscoord y = GetContinuationOffset();
   1.846 +    aMetrics.Height() -= y + aReflowState.ComputedPhysicalBorderPadding().top;
   1.847 +    aMetrics.Height() = std::max(0, aMetrics.Height());
   1.848 +  }
   1.849 +
   1.850 +
   1.851 +  // we have to split images if we are:
   1.852 +  //  in Paginated mode, we need to have a constrained height, and have a height larger than our available height
   1.853 +  uint32_t loadStatus = imgIRequest::STATUS_NONE;
   1.854 +  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   1.855 +  NS_ASSERTION(imageLoader, "No content node??");
   1.856 +  if (imageLoader) {
   1.857 +    nsCOMPtr<imgIRequest> currentRequest;
   1.858 +    imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   1.859 +                            getter_AddRefs(currentRequest));
   1.860 +    if (currentRequest) {
   1.861 +      currentRequest->GetImageStatus(&loadStatus);
   1.862 +    }
   1.863 +  }
   1.864 +  if (aPresContext->IsPaginated() &&
   1.865 +      ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
   1.866 +      NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() && 
   1.867 +      aMetrics.Height() > aReflowState.AvailableHeight()) { 
   1.868 +    // our desired height was greater than 0, so to avoid infinite
   1.869 +    // splitting, use 1 pixel as the min
   1.870 +    aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowState.AvailableHeight());
   1.871 +    aStatus = NS_FRAME_NOT_COMPLETE;
   1.872 +  }
   1.873 +
   1.874 +  aMetrics.SetOverflowAreasToDesiredBounds();
   1.875 +  EventStates contentState = mContent->AsElement()->State();
   1.876 +  bool imageOK = IMAGE_OK(contentState, true);
   1.877 +
   1.878 +  // Determine if the size is available
   1.879 +  bool haveSize = false;
   1.880 +  if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) {
   1.881 +    haveSize = true;
   1.882 +  }
   1.883 +
   1.884 +  if (!imageOK || !haveSize) {
   1.885 +    nsRect altFeedbackSize(0, 0,
   1.886 +                           nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)),
   1.887 +                           nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)));
   1.888 +    // We include the altFeedbackSize in our visual overflow, but not in our
   1.889 +    // scrollable overflow, since it doesn't really need to be scrolled to
   1.890 +    // outside the image.
   1.891 +    static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
   1.892 +    nsRect& visualOverflow = aMetrics.VisualOverflow();
   1.893 +    visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
   1.894 +  }
   1.895 +  FinishAndStoreOverflow(&aMetrics);
   1.896 +
   1.897 +  if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
   1.898 +    nsIPresShell* shell = PresContext()->PresShell();
   1.899 +    mReflowCallbackPosted = true;
   1.900 +    shell->PostReflowCallback(this);
   1.901 +  }
   1.902 +
   1.903 +  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
   1.904 +                  ("exit nsImageFrame::Reflow: size=%d,%d",
   1.905 +                  aMetrics.Width(), aMetrics.Height()));
   1.906 +  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
   1.907 +  return NS_OK;
   1.908 +}
   1.909 +
   1.910 +bool
   1.911 +nsImageFrame::ReflowFinished()
   1.912 +{
   1.913 +  mReflowCallbackPosted = false;
   1.914 +
   1.915 +  nsLayoutUtils::UpdateImageVisibilityForFrame(this);
   1.916 +
   1.917 +  return false;
   1.918 +}
   1.919 +
   1.920 +void
   1.921 +nsImageFrame::ReflowCallbackCanceled()
   1.922 +{
   1.923 +  mReflowCallbackPosted = false;
   1.924 +}
   1.925 +
   1.926 +// Computes the width of the specified string. aMaxWidth specifies the maximum
   1.927 +// width available. Once this limit is reached no more characters are measured.
   1.928 +// The number of characters that fit within the maximum width are returned in
   1.929 +// aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
   1.930 +// into the rendering context before this is called (for performance). MMP
   1.931 +nscoord
   1.932 +nsImageFrame::MeasureString(const char16_t*     aString,
   1.933 +                            int32_t              aLength,
   1.934 +                            nscoord              aMaxWidth,
   1.935 +                            uint32_t&            aMaxFit,
   1.936 +                            nsRenderingContext& aContext)
   1.937 +{
   1.938 +  nscoord totalWidth = 0;
   1.939 +  aContext.SetTextRunRTL(false);
   1.940 +  nscoord spaceWidth = aContext.GetWidth(' ');
   1.941 +
   1.942 +  aMaxFit = 0;
   1.943 +  while (aLength > 0) {
   1.944 +    // Find the next place we can line break
   1.945 +    uint32_t  len = aLength;
   1.946 +    bool      trailingSpace = false;
   1.947 +    for (int32_t i = 0; i < aLength; i++) {
   1.948 +      if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
   1.949 +        len = i;  // don't include the space when measuring
   1.950 +        trailingSpace = true;
   1.951 +        break;
   1.952 +      }
   1.953 +    }
   1.954 +  
   1.955 +    // Measure this chunk of text, and see if it fits
   1.956 +    nscoord width =
   1.957 +      nsLayoutUtils::GetStringWidth(this, &aContext, aString, len);
   1.958 +    bool    fits = (totalWidth + width) <= aMaxWidth;
   1.959 +
   1.960 +    // If it fits on the line, or it's the first word we've processed then
   1.961 +    // include it
   1.962 +    if (fits || (0 == totalWidth)) {
   1.963 +      // New piece fits
   1.964 +      totalWidth += width;
   1.965 +
   1.966 +      // If there's a trailing space then see if it fits as well
   1.967 +      if (trailingSpace) {
   1.968 +        if ((totalWidth + spaceWidth) <= aMaxWidth) {
   1.969 +          totalWidth += spaceWidth;
   1.970 +        } else {
   1.971 +          // Space won't fit. Leave it at the end but don't include it in
   1.972 +          // the width
   1.973 +          fits = false;
   1.974 +        }
   1.975 +
   1.976 +        len++;
   1.977 +      }
   1.978 +
   1.979 +      aMaxFit += len;
   1.980 +      aString += len;
   1.981 +      aLength -= len;
   1.982 +    }
   1.983 +
   1.984 +    if (!fits) {
   1.985 +      break;
   1.986 +    }
   1.987 +  }
   1.988 +  return totalWidth;
   1.989 +}
   1.990 +
   1.991 +// Formats the alt-text to fit within the specified rectangle. Breaks lines
   1.992 +// between words if a word would extend past the edge of the rectangle
   1.993 +void
   1.994 +nsImageFrame::DisplayAltText(nsPresContext*      aPresContext,
   1.995 +                             nsRenderingContext& aRenderingContext,
   1.996 +                             const nsString&      aAltText,
   1.997 +                             const nsRect&        aRect)
   1.998 +{
   1.999 +  // Set font and color
  1.1000 +  aRenderingContext.SetColor(StyleColor()->mColor);
  1.1001 +  nsRefPtr<nsFontMetrics> fm;
  1.1002 +  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
  1.1003 +    nsLayoutUtils::FontSizeInflationFor(this));
  1.1004 +  aRenderingContext.SetFont(fm);
  1.1005 +
  1.1006 +  // Format the text to display within the formatting rect
  1.1007 +
  1.1008 +  nscoord maxAscent = fm->MaxAscent();
  1.1009 +  nscoord maxDescent = fm->MaxDescent();
  1.1010 +  nscoord height = fm->MaxHeight();
  1.1011 +
  1.1012 +  // XXX It would be nice if there was a way to have the font metrics tell
  1.1013 +  // use where to break the text given a maximum width. At a minimum we need
  1.1014 +  // to be able to get the break character...
  1.1015 +  const char16_t* str = aAltText.get();
  1.1016 +  int32_t          strLen = aAltText.Length();
  1.1017 +  nscoord          y = aRect.y;
  1.1018 +
  1.1019 +  if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
  1.1020 +    aPresContext->SetBidiEnabled();
  1.1021 +  }
  1.1022 +
  1.1023 +  // Always show the first line, even if we have to clip it below
  1.1024 +  bool firstLine = true;
  1.1025 +  while ((strLen > 0) && (firstLine || (y + maxDescent) < aRect.YMost())) {
  1.1026 +    // Determine how much of the text to display on this line
  1.1027 +    uint32_t  maxFit;  // number of characters that fit
  1.1028 +    nscoord strWidth = MeasureString(str, strLen, aRect.width, maxFit,
  1.1029 +                                     aRenderingContext);
  1.1030 +    
  1.1031 +    // Display the text
  1.1032 +    nsresult rv = NS_ERROR_FAILURE;
  1.1033 +
  1.1034 +    if (aPresContext->BidiEnabled()) {
  1.1035 +      const nsStyleVisibility* vis = StyleVisibility();
  1.1036 +      if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
  1.1037 +        rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_RTL,
  1.1038 +                                         aPresContext, aRenderingContext,
  1.1039 +                                         aRenderingContext,
  1.1040 +                                         aRect.XMost() - strWidth, y + maxAscent);
  1.1041 +      else
  1.1042 +        rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_LTR,
  1.1043 +                                         aPresContext, aRenderingContext,
  1.1044 +                                         aRenderingContext,
  1.1045 +                                         aRect.x, y + maxAscent);
  1.1046 +    }
  1.1047 +    if (NS_FAILED(rv))
  1.1048 +      aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent);
  1.1049 +
  1.1050 +    // Move to the next line
  1.1051 +    str += maxFit;
  1.1052 +    strLen -= maxFit;
  1.1053 +    y += height;
  1.1054 +    firstLine = false;
  1.1055 +  }
  1.1056 +}
  1.1057 +
  1.1058 +struct nsRecessedBorder : public nsStyleBorder {
  1.1059 +  nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
  1.1060 +    : nsStyleBorder(aPresContext)
  1.1061 +  {
  1.1062 +    NS_FOR_CSS_SIDES(side) {
  1.1063 +      // Note: use SetBorderColor here because we want to make sure
  1.1064 +      // the "special" flags are unset.
  1.1065 +      SetBorderColor(side, NS_RGB(0, 0, 0));
  1.1066 +      mBorder.Side(side) = aBorderWidth;
  1.1067 +      // Note: use SetBorderStyle here because we want to affect
  1.1068 +      // mComputedBorder
  1.1069 +      SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
  1.1070 +    }
  1.1071 +  }
  1.1072 +};
  1.1073 +
  1.1074 +class nsDisplayAltFeedback : public nsDisplayItem {
  1.1075 +public:
  1.1076 +  nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
  1.1077 +    : nsDisplayItem(aBuilder, aFrame) {}
  1.1078 +
  1.1079 +  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
  1.1080 +                           bool* aSnap) MOZ_OVERRIDE
  1.1081 +  {
  1.1082 +    *aSnap = false;
  1.1083 +    return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
  1.1084 +  }
  1.1085 +
  1.1086 +  virtual void Paint(nsDisplayListBuilder* aBuilder,
  1.1087 +                     nsRenderingContext* aCtx) MOZ_OVERRIDE
  1.1088 +  {
  1.1089 +    nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
  1.1090 +    EventStates state = f->GetContent()->AsElement()->State();
  1.1091 +    f->DisplayAltFeedback(*aCtx,
  1.1092 +                          mVisibleRect,
  1.1093 +                          IMAGE_OK(state, true)
  1.1094 +                             ? nsImageFrame::gIconLoad->mLoadingImage
  1.1095 +                             : nsImageFrame::gIconLoad->mBrokenImage,
  1.1096 +                          ToReferenceFrame());
  1.1097 +
  1.1098 +  }
  1.1099 +
  1.1100 +  NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
  1.1101 +};
  1.1102 +
  1.1103 +void
  1.1104 +nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext,
  1.1105 +                                 const nsRect&        aDirtyRect,
  1.1106 +                                 imgIRequest*         aRequest,
  1.1107 +                                 nsPoint              aPt)
  1.1108 +{
  1.1109 +  // We should definitely have a gIconLoad here.
  1.1110 +  NS_ABORT_IF_FALSE(gIconLoad, "How did we succeed in Init then?");
  1.1111 +
  1.1112 +  // Calculate the inner area
  1.1113 +  nsRect  inner = GetInnerArea() + aPt;
  1.1114 +
  1.1115 +  // Display a recessed one pixel border
  1.1116 +  nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
  1.1117 +
  1.1118 +  // if inner area is empty, then make it big enough for at least the icon
  1.1119 +  if (inner.IsEmpty()){
  1.1120 +    inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)),
  1.1121 +                 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)));
  1.1122 +  }
  1.1123 +
  1.1124 +  // Make sure we have enough room to actually render the border within
  1.1125 +  // our frame bounds
  1.1126 +  if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
  1.1127 +    return;
  1.1128 +  }
  1.1129 +
  1.1130 +  // Paint the border
  1.1131 +  nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
  1.1132 +  nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext,
  1.1133 +                                             this, inner, inner,
  1.1134 +                                             recessedBorder, mStyleContext);
  1.1135 +
  1.1136 +  // Adjust the inner rect to account for the one pixel recessed border,
  1.1137 +  // and a six pixel padding on each edge
  1.1138 +  inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH), 
  1.1139 +                nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH));
  1.1140 +  if (inner.IsEmpty()) {
  1.1141 +    return;
  1.1142 +  }
  1.1143 +
  1.1144 +  // Clip so we don't render outside the inner rect
  1.1145 +  aRenderingContext.PushState();
  1.1146 +  aRenderingContext.IntersectClip(inner);
  1.1147 +
  1.1148 +  // Check if we should display image placeholders
  1.1149 +  if (gIconLoad->mPrefShowPlaceholders) {
  1.1150 +    const nsStyleVisibility* vis = StyleVisibility();
  1.1151 +    nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
  1.1152 +
  1.1153 +    bool iconUsed = false;
  1.1154 +
  1.1155 +    // If we weren't previously displaying an icon, register ourselves
  1.1156 +    // as an observer for load and animation updates and flag that we're
  1.1157 +    // doing so now.
  1.1158 +    if (aRequest && !mDisplayingIcon) {
  1.1159 +      gIconLoad->AddIconObserver(this);
  1.1160 +      mDisplayingIcon = true;
  1.1161 +    }
  1.1162 +
  1.1163 +
  1.1164 +    // If the icon in question is loaded and decoded, draw it
  1.1165 +    uint32_t imageStatus = 0;
  1.1166 +    if (aRequest)
  1.1167 +      aRequest->GetImageStatus(&imageStatus);
  1.1168 +    if (imageStatus & imgIRequest::STATUS_FRAME_COMPLETE) {
  1.1169 +      nsCOMPtr<imgIContainer> imgCon;
  1.1170 +      aRequest->GetImage(getter_AddRefs(imgCon));
  1.1171 +      NS_ABORT_IF_FALSE(imgCon, "Frame Complete, but no image container?");
  1.1172 +      nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
  1.1173 +                  inner.XMost() - size : inner.x,
  1.1174 +                  inner.y, size, size);
  1.1175 +      nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
  1.1176 +        nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
  1.1177 +        nullptr, imgIContainer::FLAG_NONE);
  1.1178 +      iconUsed = true;
  1.1179 +    }
  1.1180 +
  1.1181 +    // if we could not draw the icon, flag that we're waiting for it and
  1.1182 +    // just draw some graffiti in the mean time
  1.1183 +    if (!iconUsed) {
  1.1184 +      nscoord iconXPos = (vis->mDirection ==   NS_STYLE_DIRECTION_RTL) ?
  1.1185 +                         inner.XMost() - size : inner.x;
  1.1186 +      nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
  1.1187 +      aRenderingContext.DrawRect(iconXPos, inner.y,size,size);
  1.1188 +      aRenderingContext.PushState();
  1.1189 +      aRenderingContext.SetColor(NS_RGB(0xFF,0,0));
  1.1190 +      aRenderingContext.FillEllipse(size/2 + iconXPos, size/2 + inner.y,
  1.1191 +                                    size/2 - twoPX, size/2 - twoPX);
  1.1192 +      aRenderingContext.PopState();
  1.1193 +    }
  1.1194 +
  1.1195 +    // Reduce the inner rect by the width of the icon, and leave an
  1.1196 +    // additional ICON_PADDING pixels for padding
  1.1197 +    int32_t iconWidth = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
  1.1198 +    if (vis->mDirection != NS_STYLE_DIRECTION_RTL)
  1.1199 +      inner.x += iconWidth;
  1.1200 +    inner.width -= iconWidth;
  1.1201 +  }
  1.1202 +
  1.1203 +  // If there's still room, display the alt-text
  1.1204 +  if (!inner.IsEmpty()) {
  1.1205 +    nsIContent* content = GetContent();
  1.1206 +    if (content) {
  1.1207 +      nsXPIDLString altText;
  1.1208 +      nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(),
  1.1209 +                                                 altText);
  1.1210 +      DisplayAltText(PresContext(), aRenderingContext, altText, inner);
  1.1211 +    }
  1.1212 +  }
  1.1213 +
  1.1214 +  aRenderingContext.PopState();
  1.1215 +}
  1.1216 +
  1.1217 +#ifdef DEBUG
  1.1218 +static void PaintDebugImageMap(nsIFrame* aFrame, nsRenderingContext* aCtx,
  1.1219 +     const nsRect& aDirtyRect, nsPoint aPt) {
  1.1220 +  nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
  1.1221 +  nsRect inner = f->GetInnerArea() + aPt;
  1.1222 +
  1.1223 +  aCtx->SetColor(NS_RGB(0, 0, 0));
  1.1224 +  aCtx->PushState();
  1.1225 +  aCtx->Translate(inner.TopLeft());
  1.1226 +  f->GetImageMap()->Draw(aFrame, *aCtx);
  1.1227 +  aCtx->PopState();
  1.1228 +}
  1.1229 +#endif
  1.1230 +
  1.1231 +void
  1.1232 +nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
  1.1233 +                      nsRenderingContext* aCtx) {
  1.1234 +  uint32_t flags = imgIContainer::FLAG_NONE;
  1.1235 +  if (aBuilder->ShouldSyncDecodeImages()) {
  1.1236 +    flags |= imgIContainer::FLAG_SYNC_DECODE;
  1.1237 +  }
  1.1238 +  if (aBuilder->IsPaintingToWindow()) {
  1.1239 +    flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
  1.1240 +  }
  1.1241 +  static_cast<nsImageFrame*>(mFrame)->
  1.1242 +    PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
  1.1243 +}
  1.1244 +
  1.1245 +void
  1.1246 +nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  1.1247 +                                          const nsDisplayItemGeometry* aGeometry,
  1.1248 +                                          nsRegion* aInvalidRegion)
  1.1249 +{
  1.1250 +  if (aBuilder->ShouldSyncDecodeImages() && mImage && !mImage->IsDecoded()) {
  1.1251 +    bool snap;
  1.1252 +    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
  1.1253 +  }
  1.1254 +
  1.1255 +  nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
  1.1256 +}
  1.1257 +
  1.1258 +already_AddRefed<ImageContainer>
  1.1259 +nsDisplayImage::GetContainer(LayerManager* aManager,
  1.1260 +                             nsDisplayListBuilder* aBuilder)
  1.1261 +{
  1.1262 +  nsRefPtr<ImageContainer> container;
  1.1263 +  nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
  1.1264 +  NS_ENSURE_SUCCESS(rv, nullptr);
  1.1265 +  return container.forget();
  1.1266 +}
  1.1267 +
  1.1268 +gfxRect
  1.1269 +nsDisplayImage::GetDestRect()
  1.1270 +{
  1.1271 +  int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
  1.1272 +  nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
  1.1273 +
  1.1274 +  nsRect dest = imageFrame->GetInnerArea() + ToReferenceFrame();
  1.1275 +  gfxRect destRect(dest.x, dest.y, dest.width, dest.height);
  1.1276 +  destRect.ScaleInverse(factor); 
  1.1277 +
  1.1278 +  return destRect;
  1.1279 +}
  1.1280 +
  1.1281 +LayerState
  1.1282 +nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
  1.1283 +                              LayerManager* aManager,
  1.1284 +                              const ContainerLayerParameters& aParameters)
  1.1285 +{
  1.1286 +  bool animated = false;
  1.1287 +  if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
  1.1288 +      mImage->GetType() != imgIContainer::TYPE_RASTER ||
  1.1289 +      NS_FAILED(mImage->GetAnimated(&animated)) ||
  1.1290 +      !animated) {
  1.1291 +    if (!aManager->IsCompositingCheap() ||
  1.1292 +        !nsLayoutUtils::GPUImageScalingEnabled()) {
  1.1293 +      return LAYER_NONE;
  1.1294 +    }
  1.1295 +  }
  1.1296 +
  1.1297 +  if (!animated) {
  1.1298 +    int32_t imageWidth;
  1.1299 +    int32_t imageHeight;
  1.1300 +    mImage->GetWidth(&imageWidth);
  1.1301 +    mImage->GetHeight(&imageHeight);
  1.1302 +
  1.1303 +    NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
  1.1304 +
  1.1305 +    gfxRect destRect = GetDestRect();
  1.1306 +
  1.1307 +    destRect.width *= aParameters.mXScale;
  1.1308 +    destRect.height *= aParameters.mYScale;
  1.1309 +
  1.1310 +    // Calculate the scaling factor for the frame.
  1.1311 +    gfxSize scale = gfxSize(destRect.width / imageWidth,
  1.1312 +                            destRect.height / imageHeight);
  1.1313 +
  1.1314 +    // If we are not scaling at all, no point in separating this into a layer.
  1.1315 +    if (scale.width == 1.0f && scale.height == 1.0f) {
  1.1316 +      return LAYER_NONE;
  1.1317 +    }
  1.1318 +
  1.1319 +    // If the target size is pretty small, no point in using a layer.
  1.1320 +    if (destRect.width * destRect.height < 64 * 64) {
  1.1321 +      return LAYER_NONE;
  1.1322 +    }
  1.1323 +  }
  1.1324 +
  1.1325 +  nsRefPtr<ImageContainer> container;
  1.1326 +  mImage->GetImageContainer(aManager, getter_AddRefs(container));
  1.1327 +  if (!container) {
  1.1328 +    return LAYER_NONE;
  1.1329 +  }
  1.1330 +
  1.1331 +  return LAYER_ACTIVE;
  1.1332 +}
  1.1333 +
  1.1334 +already_AddRefed<Layer>
  1.1335 +nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
  1.1336 +                           LayerManager* aManager,
  1.1337 +                           const ContainerLayerParameters& aParameters)
  1.1338 +{
  1.1339 +  nsRefPtr<ImageContainer> container;
  1.1340 +  nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
  1.1341 +  NS_ENSURE_SUCCESS(rv, nullptr);
  1.1342 +
  1.1343 +  nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
  1.1344 +    (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
  1.1345 +  if (!layer) {
  1.1346 +    layer = aManager->CreateImageLayer();
  1.1347 +    if (!layer)
  1.1348 +      return nullptr;
  1.1349 +  }
  1.1350 +  layer->SetContainer(container);
  1.1351 +  ConfigureLayer(layer, aParameters.mOffset);
  1.1352 +  return layer.forget();
  1.1353 +}
  1.1354 +
  1.1355 +void
  1.1356 +nsDisplayImage::ConfigureLayer(ImageLayer *aLayer, const nsIntPoint& aOffset)
  1.1357 +{
  1.1358 +  aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
  1.1359 +
  1.1360 +  int32_t imageWidth;
  1.1361 +  int32_t imageHeight;
  1.1362 +  mImage->GetWidth(&imageWidth);
  1.1363 +  mImage->GetHeight(&imageHeight);
  1.1364 +
  1.1365 +  NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
  1.1366 +
  1.1367 +  const gfxRect destRect = GetDestRect();
  1.1368 +
  1.1369 +  gfx::Matrix transform;
  1.1370 +  gfxPoint p = destRect.TopLeft() + aOffset;
  1.1371 +  transform.Translate(p.x, p.y);
  1.1372 +  transform.Scale(destRect.Width()/imageWidth,
  1.1373 +                  destRect.Height()/imageHeight);
  1.1374 +  aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
  1.1375 +  aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight));
  1.1376 +}
  1.1377 +
  1.1378 +void
  1.1379 +nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
  1.1380 +                         const nsRect& aDirtyRect, imgIContainer* aImage,
  1.1381 +                         uint32_t aFlags)
  1.1382 +{
  1.1383 +  // Render the image into our content area (the area inside
  1.1384 +  // the borders and padding)
  1.1385 +  NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
  1.1386 +  nsRect inner = GetInnerArea() + aPt;
  1.1387 +  nsRect dest(inner.TopLeft(), mComputedSize);
  1.1388 +  dest.y -= GetContinuationOffset();
  1.1389 +
  1.1390 +  nsLayoutUtils::DrawSingleImage(&aRenderingContext, aImage,
  1.1391 +    nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
  1.1392 +    nullptr, aFlags);
  1.1393 +
  1.1394 +  nsImageMap* map = GetImageMap();
  1.1395 +  if (nullptr != map) {
  1.1396 +    aRenderingContext.PushState();
  1.1397 +    aRenderingContext.Translate(inner.TopLeft());
  1.1398 +    aRenderingContext.SetColor(NS_RGB(255, 255, 255));
  1.1399 +    aRenderingContext.SetLineStyle(nsLineStyle_kSolid);
  1.1400 +    map->Draw(this, aRenderingContext);
  1.1401 +    aRenderingContext.SetColor(NS_RGB(0, 0, 0));
  1.1402 +    aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
  1.1403 +    map->Draw(this, aRenderingContext);
  1.1404 +    aRenderingContext.PopState();
  1.1405 +  }
  1.1406 +}
  1.1407 +
  1.1408 +void
  1.1409 +nsImageFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  1.1410 +                               const nsRect&           aDirtyRect,
  1.1411 +                               const nsDisplayListSet& aLists)
  1.1412 +{
  1.1413 +  if (!IsVisibleForPainting(aBuilder))
  1.1414 +    return;
  1.1415 +
  1.1416 +  DisplayBorderBackgroundOutline(aBuilder, aLists);
  1.1417 +
  1.1418 +  DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
  1.1419 +    clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT);
  1.1420 +
  1.1421 +  if (mComputedSize.width != 0 && mComputedSize.height != 0) {
  1.1422 +    nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
  1.1423 +    NS_ASSERTION(imageLoader, "Not an image loading content?");
  1.1424 +
  1.1425 +    nsCOMPtr<imgIRequest> currentRequest;
  1.1426 +    if (imageLoader) {
  1.1427 +      imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
  1.1428 +                              getter_AddRefs(currentRequest));
  1.1429 +    }
  1.1430 +
  1.1431 +    EventStates contentState = mContent->AsElement()->State();
  1.1432 +    bool imageOK = IMAGE_OK(contentState, true);
  1.1433 +
  1.1434 +    // XXX(seth): The SizeIsAvailable check here should not be necessary - the
  1.1435 +    // intention is that a non-null mImage means we have a size, but there is
  1.1436 +    // currently some code that violates this invariant.
  1.1437 +    if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) {
  1.1438 +      // No image yet, or image load failed. Draw the alt-text and an icon
  1.1439 +      // indicating the status
  1.1440 +      aLists.Content()->AppendNewToTop(new (aBuilder)
  1.1441 +        nsDisplayAltFeedback(aBuilder, this));
  1.1442 +    } else {
  1.1443 +      aLists.Content()->AppendNewToTop(new (aBuilder)
  1.1444 +        nsDisplayImage(aBuilder, this, mImage));
  1.1445 +
  1.1446 +      // If we were previously displaying an icon, we're not anymore
  1.1447 +      if (mDisplayingIcon) {
  1.1448 +        gIconLoad->RemoveIconObserver(this);
  1.1449 +        mDisplayingIcon = false;
  1.1450 +      }
  1.1451 +
  1.1452 +#ifdef DEBUG
  1.1453 +      if (GetShowFrameBorders() && GetImageMap()) {
  1.1454 +        aLists.Outlines()->AppendNewToTop(new (aBuilder)
  1.1455 +          nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap",
  1.1456 +                           nsDisplayItem::TYPE_DEBUG_IMAGE_MAP));
  1.1457 +      }
  1.1458 +#endif
  1.1459 +    }
  1.1460 +  }
  1.1461 +
  1.1462 +  if (ShouldDisplaySelection()) {
  1.1463 +    DisplaySelectionOverlay(aBuilder, aLists.Content(),
  1.1464 +                            nsISelectionDisplay::DISPLAY_IMAGES);
  1.1465 +  }
  1.1466 +}
  1.1467 +
  1.1468 +bool
  1.1469 +nsImageFrame::ShouldDisplaySelection()
  1.1470 +{
  1.1471 +  // XXX what on EARTH is this code for?
  1.1472 +  nsresult result;
  1.1473 +  nsPresContext* presContext = PresContext();
  1.1474 +  int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
  1.1475 +  if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
  1.1476 +    return false;//no need to check the blue border, we cannot be drawn selected
  1.1477 +//insert hook here for image selection drawing
  1.1478 +#if IMAGE_EDITOR_CHECK
  1.1479 +  //check to see if this frame is in an editor context
  1.1480 +  //isEditor check. this needs to be changed to have better way to check
  1.1481 +  if (displaySelection == nsISelectionDisplay::DISPLAY_ALL) 
  1.1482 +  {
  1.1483 +    nsCOMPtr<nsISelectionController> selCon;
  1.1484 +    result = GetSelectionController(presContext, getter_AddRefs(selCon));
  1.1485 +    if (NS_SUCCEEDED(result) && selCon)
  1.1486 +    {
  1.1487 +      nsCOMPtr<nsISelection> selection;
  1.1488 +      result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  1.1489 +      if (NS_SUCCEEDED(result) && selection)
  1.1490 +      {
  1.1491 +        int32_t rangeCount;
  1.1492 +        selection->GetRangeCount(&rangeCount);
  1.1493 +        if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
  1.1494 +        {
  1.1495 +          nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
  1.1496 +          if (parentContent)
  1.1497 +          {
  1.1498 +            int32_t thisOffset = parentContent->IndexOf(mContent);
  1.1499 +            nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent);
  1.1500 +            nsCOMPtr<nsIDOMNode> rangeNode;
  1.1501 +            int32_t rangeOffset;
  1.1502 +            nsCOMPtr<nsIDOMRange> range;
  1.1503 +            selection->GetRangeAt(0,getter_AddRefs(range));
  1.1504 +            if (range)
  1.1505 +            {
  1.1506 +              range->GetStartContainer(getter_AddRefs(rangeNode));
  1.1507 +              range->GetStartOffset(&rangeOffset);
  1.1508 +
  1.1509 +              if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset)
  1.1510 +              {
  1.1511 +                range->GetEndContainer(getter_AddRefs(rangeNode));
  1.1512 +                range->GetEndOffset(&rangeOffset);
  1.1513 +                if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only
  1.1514 +                  return false; //do not allow nsFrame do draw any further selection
  1.1515 +              }
  1.1516 +            }
  1.1517 +          }
  1.1518 +        }
  1.1519 +      }
  1.1520 +    }
  1.1521 +  }
  1.1522 +#endif
  1.1523 +  return true;
  1.1524 +}
  1.1525 +
  1.1526 +nsImageMap*
  1.1527 +nsImageFrame::GetImageMap()
  1.1528 +{
  1.1529 +  if (!mImageMap) {
  1.1530 +    nsIContent* map = GetMapElement();
  1.1531 +    if (map) {
  1.1532 +      mImageMap = new nsImageMap();
  1.1533 +      NS_ADDREF(mImageMap);
  1.1534 +      mImageMap->Init(this, map);
  1.1535 +    }
  1.1536 +  }
  1.1537 +
  1.1538 +  return mImageMap;
  1.1539 +}
  1.1540 +
  1.1541 +bool
  1.1542 +nsImageFrame::IsServerImageMap()
  1.1543 +{
  1.1544 +  return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap);
  1.1545 +}
  1.1546 +
  1.1547 +// Translate an point that is relative to our frame
  1.1548 +// into a localized pixel coordinate that is relative to the
  1.1549 +// content area of this frame (inside the border+padding).
  1.1550 +void
  1.1551 +nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
  1.1552 +                                   nsIntPoint&     aResult)
  1.1553 +{
  1.1554 +  nscoord x = aPoint.x;
  1.1555 +  nscoord y = aPoint.y;
  1.1556 +
  1.1557 +  // Subtract out border and padding here so that the coordinates are
  1.1558 +  // now relative to the content area of this frame.
  1.1559 +  nsRect inner = GetInnerArea();
  1.1560 +  x -= inner.x;
  1.1561 +  y -= inner.y;
  1.1562 +
  1.1563 +  aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
  1.1564 +  aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
  1.1565 +}
  1.1566 +
  1.1567 +bool
  1.1568 +nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
  1.1569 +                                         nsIContent** aNode)
  1.1570 +{
  1.1571 +  bool status = false;
  1.1572 +  aTarget.Truncate();
  1.1573 +  *aHref = nullptr;
  1.1574 +  *aNode = nullptr;
  1.1575 +
  1.1576 +  // Walk up the content tree, looking for an nsIDOMAnchorElement
  1.1577 +  for (nsIContent* content = mContent->GetParent();
  1.1578 +       content; content = content->GetParent()) {
  1.1579 +    nsCOMPtr<dom::Link> link(do_QueryInterface(content));
  1.1580 +    if (link) {
  1.1581 +      nsCOMPtr<nsIURI> href = content->GetHrefURI();
  1.1582 +      if (href) {
  1.1583 +        href->Clone(aHref);
  1.1584 +      }
  1.1585 +      status = (*aHref != nullptr);
  1.1586 +
  1.1587 +      nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
  1.1588 +      if (anchor) {
  1.1589 +        anchor->GetTarget(aTarget);
  1.1590 +      }
  1.1591 +      NS_ADDREF(*aNode = content);
  1.1592 +      break;
  1.1593 +    }
  1.1594 +  }
  1.1595 +  return status;
  1.1596 +}
  1.1597 +
  1.1598 +nsresult  
  1.1599 +nsImageFrame::GetContentForEvent(WidgetEvent* aEvent,
  1.1600 +                                 nsIContent** aContent)
  1.1601 +{
  1.1602 +  NS_ENSURE_ARG_POINTER(aContent);
  1.1603 +
  1.1604 +  nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
  1.1605 +  if (f != this) {
  1.1606 +    return f->GetContentForEvent(aEvent, aContent);
  1.1607 +  }
  1.1608 +
  1.1609 +  // XXX We need to make this special check for area element's capturing the
  1.1610 +  // mouse due to bug 135040. Remove it once that's fixed.
  1.1611 +  nsIContent* capturingContent =
  1.1612 +    aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
  1.1613 +                                     nullptr;
  1.1614 +  if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
  1.1615 +    *aContent = capturingContent;
  1.1616 +    NS_IF_ADDREF(*aContent);
  1.1617 +    return NS_OK;
  1.1618 +  }
  1.1619 +
  1.1620 +  nsImageMap* map = GetImageMap();
  1.1621 +
  1.1622 +  if (nullptr != map) {
  1.1623 +    nsIntPoint p;
  1.1624 +    TranslateEventCoords(
  1.1625 +      nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
  1.1626 +    nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
  1.1627 +    if (area) {
  1.1628 +      area.forget(aContent);
  1.1629 +      return NS_OK;
  1.1630 +    }
  1.1631 +  }
  1.1632 +
  1.1633 +  *aContent = GetContent();
  1.1634 +  NS_IF_ADDREF(*aContent);
  1.1635 +  return NS_OK;
  1.1636 +}
  1.1637 +
  1.1638 +// XXX what should clicks on transparent pixels do?
  1.1639 +nsresult
  1.1640 +nsImageFrame::HandleEvent(nsPresContext* aPresContext,
  1.1641 +                          WidgetGUIEvent* aEvent,
  1.1642 +                          nsEventStatus* aEventStatus)
  1.1643 +{
  1.1644 +  NS_ENSURE_ARG_POINTER(aEventStatus);
  1.1645 +
  1.1646 +  if ((aEvent->message == NS_MOUSE_BUTTON_UP && 
  1.1647 +       aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
  1.1648 +      aEvent->message == NS_MOUSE_MOVE) {
  1.1649 +    nsImageMap* map = GetImageMap();
  1.1650 +    bool isServerMap = IsServerImageMap();
  1.1651 +    if ((nullptr != map) || isServerMap) {
  1.1652 +      nsIntPoint p;
  1.1653 +      TranslateEventCoords(
  1.1654 +        nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
  1.1655 +      bool inside = false;
  1.1656 +      // Even though client-side image map triggering happens
  1.1657 +      // through content, we need to make sure we're not inside
  1.1658 +      // (in case we deal with a case of both client-side and
  1.1659 +      // sever-side on the same image - it happens!)
  1.1660 +      if (nullptr != map) {
  1.1661 +        inside = !!map->GetArea(p.x, p.y);
  1.1662 +      }
  1.1663 +
  1.1664 +      if (!inside && isServerMap) {
  1.1665 +
  1.1666 +        // Server side image maps use the href in a containing anchor
  1.1667 +        // element to provide the basis for the destination url.
  1.1668 +        nsCOMPtr<nsIURI> uri;
  1.1669 +        nsAutoString target;
  1.1670 +        nsCOMPtr<nsIContent> anchorNode;
  1.1671 +        if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
  1.1672 +                                       getter_AddRefs(anchorNode))) {
  1.1673 +          // XXX if the mouse is over/clicked in the border/padding area
  1.1674 +          // we should probably just pretend nothing happened. Nav4
  1.1675 +          // keeps the x,y coordinates positive as we do; IE doesn't
  1.1676 +          // bother. Both of them send the click through even when the
  1.1677 +          // mouse is over the border.
  1.1678 +          if (p.x < 0) p.x = 0;
  1.1679 +          if (p.y < 0) p.y = 0;
  1.1680 +          nsAutoCString spec;
  1.1681 +          uri->GetSpec(spec);
  1.1682 +          spec += nsPrintfCString("?%d,%d", p.x, p.y);
  1.1683 +          uri->SetSpec(spec);                
  1.1684 +          
  1.1685 +          bool clicked = false;
  1.1686 +          if (aEvent->message == NS_MOUSE_BUTTON_UP) {
  1.1687 +            *aEventStatus = nsEventStatus_eConsumeDoDefault; 
  1.1688 +            clicked = true;
  1.1689 +          }
  1.1690 +          nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
  1.1691 +                                      clicked, true, true);
  1.1692 +        }
  1.1693 +      }
  1.1694 +    }
  1.1695 +  }
  1.1696 +
  1.1697 +  return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
  1.1698 +}
  1.1699 +
  1.1700 +nsresult
  1.1701 +nsImageFrame::GetCursor(const nsPoint& aPoint,
  1.1702 +                        nsIFrame::Cursor& aCursor)
  1.1703 +{
  1.1704 +  nsImageMap* map = GetImageMap();
  1.1705 +  if (nullptr != map) {
  1.1706 +    nsIntPoint p;
  1.1707 +    TranslateEventCoords(aPoint, p);
  1.1708 +    nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
  1.1709 +    if (area) {
  1.1710 +      // Use the cursor from the style of the *area* element.
  1.1711 +      // XXX Using the image as the parent style context isn't
  1.1712 +      // technically correct, but it's probably the right thing to do
  1.1713 +      // here, since it means that areas on which the cursor isn't
  1.1714 +      // specified will inherit the style from the image.
  1.1715 +      nsRefPtr<nsStyleContext> areaStyle = 
  1.1716 +        PresContext()->PresShell()->StyleSet()->
  1.1717 +          ResolveStyleFor(area->AsElement(), StyleContext());
  1.1718 +      FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
  1.1719 +                                     aCursor);
  1.1720 +      if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
  1.1721 +        aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
  1.1722 +      }
  1.1723 +      return NS_OK;
  1.1724 +    }
  1.1725 +  }
  1.1726 +  return nsFrame::GetCursor(aPoint, aCursor);
  1.1727 +}
  1.1728 +
  1.1729 +nsresult
  1.1730 +nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
  1.1731 +                               nsIAtom* aAttribute,
  1.1732 +                               int32_t aModType)
  1.1733 +{
  1.1734 +  nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID,
  1.1735 +                                                    aAttribute, aModType);
  1.1736 +  if (NS_FAILED(rv)) {
  1.1737 +    return rv;
  1.1738 +  }
  1.1739 +  if (nsGkAtoms::alt == aAttribute)
  1.1740 +  {
  1.1741 +    PresContext()->PresShell()->FrameNeedsReflow(this,
  1.1742 +                                                 nsIPresShell::eStyleChange,
  1.1743 +                                                 NS_FRAME_IS_DIRTY);
  1.1744 +  }
  1.1745 +
  1.1746 +  return NS_OK;
  1.1747 +}
  1.1748 +
  1.1749 +nsIAtom*
  1.1750 +nsImageFrame::GetType() const
  1.1751 +{
  1.1752 +  return nsGkAtoms::imageFrame;
  1.1753 +}
  1.1754 +
  1.1755 +#ifdef DEBUG_FRAME_DUMP
  1.1756 +nsresult
  1.1757 +nsImageFrame::GetFrameName(nsAString& aResult) const
  1.1758 +{
  1.1759 +  return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
  1.1760 +}
  1.1761 +
  1.1762 +void
  1.1763 +nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
  1.1764 +{
  1.1765 +  nsCString str;
  1.1766 +  ListGeneric(str, aPrefix, aFlags);
  1.1767 +
  1.1768 +  // output the img src url
  1.1769 +  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
  1.1770 +  if (imageLoader) {
  1.1771 +    nsCOMPtr<imgIRequest> currentRequest;
  1.1772 +    imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
  1.1773 +                            getter_AddRefs(currentRequest));
  1.1774 +    if (currentRequest) {
  1.1775 +      nsCOMPtr<nsIURI> uri;
  1.1776 +      currentRequest->GetURI(getter_AddRefs(uri));
  1.1777 +      nsAutoCString uristr;
  1.1778 +      uri->GetAsciiSpec(uristr);
  1.1779 +      str += nsPrintfCString(" [src=%s]", uristr.get());
  1.1780 +    }
  1.1781 +  }
  1.1782 +  fprintf_stderr(out, "%s\n", str.get());
  1.1783 +}
  1.1784 +#endif
  1.1785 +
  1.1786 +int
  1.1787 +nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
  1.1788 +{
  1.1789 +  int skip = 0;
  1.1790 +  if (nullptr != GetPrevInFlow()) {
  1.1791 +    skip |= LOGICAL_SIDE_B_START;
  1.1792 +  }
  1.1793 +  if (nullptr != GetNextInFlow()) {
  1.1794 +    skip |= LOGICAL_SIDE_B_END;
  1.1795 +  }
  1.1796 +  return skip;
  1.1797 +}
  1.1798 +
  1.1799 +nsresult
  1.1800 +nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
  1.1801 +{
  1.1802 +  if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
  1.1803 +      mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
  1.1804 +    aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(),
  1.1805 +                 mIntrinsicSize.height.GetCoordValue());
  1.1806 +    return NS_OK;
  1.1807 +  }
  1.1808 +
  1.1809 +  return NS_ERROR_FAILURE;
  1.1810 +}
  1.1811 +
  1.1812 +nsresult
  1.1813 +nsImageFrame::LoadIcon(const nsAString& aSpec,
  1.1814 +                       nsPresContext *aPresContext,
  1.1815 +                       imgRequestProxy** aRequest)
  1.1816 +{
  1.1817 +  nsresult rv = NS_OK;
  1.1818 +  NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
  1.1819 +  NS_PRECONDITION(aPresContext, "NULL PresContext");
  1.1820 +
  1.1821 +  if (!sIOService) {
  1.1822 +    rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
  1.1823 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1824 +  }
  1.1825 +
  1.1826 +  nsCOMPtr<nsIURI> realURI;
  1.1827 +  SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
  1.1828 + 
  1.1829 +  nsRefPtr<imgLoader> il =
  1.1830 +    nsContentUtils::GetImgLoaderForDocument(aPresContext->Document());
  1.1831 +
  1.1832 +  nsCOMPtr<nsILoadGroup> loadGroup;
  1.1833 +  GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
  1.1834 +
  1.1835 +  // For icon loads, we don't need to merge with the loadgroup flags
  1.1836 +  nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
  1.1837 +
  1.1838 +  nsCOMPtr<nsIURI> firstPartyIsolationURI;
  1.1839 +  nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc
  1.1840 +      = do_GetService(THIRDPARTYUTIL_CONTRACTID);
  1.1841 +  // XXX: Should we pass the loadgroup, too? Is document ever likely
  1.1842 +  // to be unset?
  1.1843 +  thirdPartySvc->GetFirstPartyIsolationURI(nullptr, aPresContext->Document(),
  1.1844 +                                           getter_AddRefs(firstPartyIsolationURI));
  1.1845 + 
  1.1846 +  return il->LoadImage(realURI,                /* icon URI */
  1.1847 +                       firstPartyIsolationURI, /* initial document URI; this is only
  1.1848 +                                                  relevant for cookies, so does not
  1.1849 +                                                  apply to icons. */
  1.1850 +                       nullptr,                /* referrer (not relevant for icons) */
  1.1851 +                       nullptr,                /* principal (not relevant for icons) */
  1.1852 +                       loadGroup,
  1.1853 +                       gIconLoad,
  1.1854 +                       nullptr,                /* Not associated with any particular document */
  1.1855 +                       loadFlags,
  1.1856 +                       nullptr,
  1.1857 +                       nullptr,                /* channel policy not needed */
  1.1858 +                       EmptyString(),
  1.1859 +                       aRequest);
  1.1860 +}
  1.1861 +
  1.1862 +void
  1.1863 +nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
  1.1864 +{
  1.1865 +  if (mContent) {
  1.1866 +    NS_ASSERTION(mContent->GetDocument(),
  1.1867 +                 "Frame still alive after content removed from document!");
  1.1868 +    aCharset = mContent->GetDocument()->GetDocumentCharacterSet();
  1.1869 +  }
  1.1870 +}
  1.1871 +
  1.1872 +void
  1.1873 +nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
  1.1874 +                         nsIURI **aURI)
  1.1875 +{
  1.1876 +  nsCOMPtr<nsIURI> baseURI;
  1.1877 +  if (mContent) {
  1.1878 +    baseURI = mContent->GetBaseURI();
  1.1879 +  }
  1.1880 +  nsAutoCString charset;
  1.1881 +  GetDocumentCharacterSet(charset);
  1.1882 +  NS_NewURI(aURI, aSpec, 
  1.1883 +            charset.IsEmpty() ? nullptr : charset.get(), 
  1.1884 +            baseURI, aIOService);
  1.1885 +}
  1.1886 +
  1.1887 +void
  1.1888 +nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
  1.1889 +{
  1.1890 +  if (!aPresContext)
  1.1891 +    return;
  1.1892 +
  1.1893 +  NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
  1.1894 +
  1.1895 +  nsIPresShell *shell = aPresContext->GetPresShell();
  1.1896 +
  1.1897 +  if (!shell)
  1.1898 +    return;
  1.1899 +
  1.1900 +  nsIDocument *doc = shell->GetDocument();
  1.1901 +  if (!doc)
  1.1902 +    return;
  1.1903 +
  1.1904 +  *aLoadGroup = doc->GetDocumentLoadGroup().take();
  1.1905 +}
  1.1906 +
  1.1907 +nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
  1.1908 +{
  1.1909 +  NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
  1.1910 +
  1.1911 +  NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png");
  1.1912 +  NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png");
  1.1913 +
  1.1914 +  gIconLoad = new IconLoad();
  1.1915 +  NS_ADDREF(gIconLoad);
  1.1916 +
  1.1917 +  nsresult rv;
  1.1918 +  // create a loader and load the images
  1.1919 +  rv = LoadIcon(loadingSrc,
  1.1920 +                aPresContext,
  1.1921 +                getter_AddRefs(gIconLoad->mLoadingImage));
  1.1922 +  if (NS_FAILED(rv)) {
  1.1923 +    return rv;
  1.1924 +  }
  1.1925 +
  1.1926 +  rv = LoadIcon(brokenSrc,
  1.1927 +                aPresContext,
  1.1928 +                getter_AddRefs(gIconLoad->mBrokenImage));
  1.1929 +  return rv;
  1.1930 +}
  1.1931 +
  1.1932 +NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver,
  1.1933 +                  imgINotificationObserver)
  1.1934 +
  1.1935 +static const char* kIconLoadPrefs[] = {
  1.1936 +  "browser.display.force_inline_alttext",
  1.1937 +  "browser.display.show_image_placeholders",
  1.1938 +  nullptr
  1.1939 +};
  1.1940 +
  1.1941 +nsImageFrame::IconLoad::IconLoad()
  1.1942 +{
  1.1943 +  // register observers
  1.1944 +  Preferences::AddStrongObservers(this, kIconLoadPrefs);
  1.1945 +  GetPrefs();
  1.1946 +}
  1.1947 +
  1.1948 +void
  1.1949 +nsImageFrame::IconLoad::Shutdown()
  1.1950 +{
  1.1951 +  Preferences::RemoveObservers(this, kIconLoadPrefs);
  1.1952 +  // in case the pref service releases us later
  1.1953 +  if (mLoadingImage) {
  1.1954 +    mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
  1.1955 +    mLoadingImage = nullptr;
  1.1956 +  }
  1.1957 +  if (mBrokenImage) {
  1.1958 +    mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
  1.1959 +    mBrokenImage = nullptr;
  1.1960 +  }
  1.1961 +}
  1.1962 +
  1.1963 +NS_IMETHODIMP
  1.1964 +nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
  1.1965 +                                const char16_t* aData)
  1.1966 +{
  1.1967 +  NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
  1.1968 +               "wrong topic");
  1.1969 +#ifdef DEBUG
  1.1970 +  // assert |aData| is one of our prefs.
  1.1971 +  for (uint32_t i = 0; i < ArrayLength(kIconLoadPrefs) ||
  1.1972 +                       (NS_NOTREACHED("wrong pref"), false); ++i)
  1.1973 +    if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
  1.1974 +      break;
  1.1975 +#endif
  1.1976 +
  1.1977 +  GetPrefs();
  1.1978 +  return NS_OK;
  1.1979 +}
  1.1980 +
  1.1981 +void nsImageFrame::IconLoad::GetPrefs()
  1.1982 +{
  1.1983 +  mPrefForceInlineAltText =
  1.1984 +    Preferences::GetBool("browser.display.force_inline_alttext");
  1.1985 +
  1.1986 +  mPrefShowPlaceholders =
  1.1987 +    Preferences::GetBool("browser.display.show_image_placeholders", true);
  1.1988 +}
  1.1989 +
  1.1990 +NS_IMETHODIMP
  1.1991 +nsImageFrame::IconLoad::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
  1.1992 +{
  1.1993 +  if (aType != imgINotificationObserver::LOAD_COMPLETE &&
  1.1994 +      aType != imgINotificationObserver::FRAME_UPDATE) {
  1.1995 +    return NS_OK;
  1.1996 +  }
  1.1997 +
  1.1998 +  nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
  1.1999 +  nsImageFrame *frame;
  1.2000 +  while (iter.HasMore()) {
  1.2001 +    frame = iter.GetNext();
  1.2002 +    frame->InvalidateFrame();
  1.2003 +  }
  1.2004 +
  1.2005 +  return NS_OK;
  1.2006 +}
  1.2007 +
  1.2008 +NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
  1.2009 +
  1.2010 +nsImageListener::nsImageListener(nsImageFrame *aFrame) :
  1.2011 +  mFrame(aFrame)
  1.2012 +{
  1.2013 +}
  1.2014 +
  1.2015 +nsImageListener::~nsImageListener()
  1.2016 +{
  1.2017 +}
  1.2018 +
  1.2019 +NS_IMETHODIMP
  1.2020 +nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
  1.2021 +{
  1.2022 +  if (!mFrame)
  1.2023 +    return NS_ERROR_FAILURE;
  1.2024 +
  1.2025 +  return mFrame->Notify(aRequest, aType, aData);
  1.2026 +}
  1.2027 +
  1.2028 +static bool
  1.2029 +IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
  1.2030 +{
  1.2031 +  if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
  1.2032 +    return false;
  1.2033 +  // Check if the parent of the closest nsBlockFrame has auto width.
  1.2034 +  nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
  1.2035 +  if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
  1.2036 +    // Assume direct parent is a table cell frame.
  1.2037 +    nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
  1.2038 +    return grandAncestor &&
  1.2039 +      grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
  1.2040 +  }
  1.2041 +  return false;
  1.2042 +}
  1.2043 +
  1.2044 +/* virtual */ void
  1.2045 +nsImageFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
  1.2046 +                                nsIFrame::InlineMinWidthData *aData)
  1.2047 +{
  1.2048 +
  1.2049 +  NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
  1.2050 +  
  1.2051 +  nsIFrame* parent = GetParent();
  1.2052 +  bool canBreak =
  1.2053 +    !CanContinueTextRun() &&
  1.2054 +    parent->StyleText()->WhiteSpaceCanWrap(parent) &&
  1.2055 +    !IsInAutoWidthTableCellForQuirk(this);
  1.2056 +
  1.2057 +  if (canBreak)
  1.2058 +    aData->OptionallyBreak(aRenderingContext);
  1.2059 + 
  1.2060 +  aData->trailingWhitespace = 0;
  1.2061 +  aData->skipWhitespace = false;
  1.2062 +  aData->trailingTextFrame = nullptr;
  1.2063 +  aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
  1.2064 +                            this, nsLayoutUtils::MIN_WIDTH);
  1.2065 +  aData->atStartOfLine = false;
  1.2066 +
  1.2067 +  if (canBreak)
  1.2068 +    aData->OptionallyBreak(aRenderingContext);
  1.2069 +
  1.2070 +}

mercurial