layout/xul/nsImageBoxFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/xul/nsImageBoxFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,771 @@
     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 +//
    1.10 +// Eric Vaughan
    1.11 +// Netscape Communications
    1.12 +//
    1.13 +// See documentation in associated header file
    1.14 +//
    1.15 +
    1.16 +#include "nsImageBoxFrame.h"
    1.17 +#include "nsGkAtoms.h"
    1.18 +#include "nsStyleContext.h"
    1.19 +#include "nsStyleConsts.h"
    1.20 +#include "nsCOMPtr.h"
    1.21 +#include "nsPresContext.h"
    1.22 +#include "nsBoxLayoutState.h"
    1.23 +
    1.24 +#include "nsHTMLParts.h"
    1.25 +#include "nsString.h"
    1.26 +#include "nsLeafFrame.h"
    1.27 +#include "nsIPresShell.h"
    1.28 +#include "nsIDocument.h"
    1.29 +#include "nsImageMap.h"
    1.30 +#include "nsILinkHandler.h"
    1.31 +#include "nsIURL.h"
    1.32 +#include "nsILoadGroup.h"
    1.33 +#include "nsContainerFrame.h"
    1.34 +#include "prprf.h"
    1.35 +#include "nsCSSRendering.h"
    1.36 +#include "nsIDOMHTMLImageElement.h"
    1.37 +#include "nsNameSpaceManager.h"
    1.38 +#include "nsTextFragment.h"
    1.39 +#include "nsIDOMHTMLMapElement.h"
    1.40 +#include "nsTransform2D.h"
    1.41 +#include "nsITheme.h"
    1.42 +
    1.43 +#include "nsIServiceManager.h"
    1.44 +#include "nsIURI.h"
    1.45 +#include "nsNetUtil.h"
    1.46 +#include "nsThreadUtils.h"
    1.47 +#include "nsDisplayList.h"
    1.48 +#include "ImageLayers.h"
    1.49 +#include "ImageContainer.h"
    1.50 +
    1.51 +#include "nsContentUtils.h"
    1.52 +
    1.53 +#include "mozilla/BasicEvents.h"
    1.54 +#include "mozilla/EventDispatcher.h"
    1.55 +
    1.56 +#define ONLOAD_CALLED_TOO_EARLY 1
    1.57 +
    1.58 +using namespace mozilla;
    1.59 +using namespace mozilla::layers;
    1.60 +
    1.61 +class nsImageBoxFrameEvent : public nsRunnable
    1.62 +{
    1.63 +public:
    1.64 +  nsImageBoxFrameEvent(nsIContent *content, uint32_t message)
    1.65 +    : mContent(content), mMessage(message) {}
    1.66 +
    1.67 +  NS_IMETHOD Run() MOZ_OVERRIDE;
    1.68 +
    1.69 +private:
    1.70 +  nsCOMPtr<nsIContent> mContent;
    1.71 +  uint32_t mMessage;
    1.72 +};
    1.73 +
    1.74 +NS_IMETHODIMP
    1.75 +nsImageBoxFrameEvent::Run()
    1.76 +{
    1.77 +  nsIPresShell *pres_shell = mContent->OwnerDoc()->GetShell();
    1.78 +  if (!pres_shell) {
    1.79 +    return NS_OK;
    1.80 +  }
    1.81 +
    1.82 +  nsRefPtr<nsPresContext> pres_context = pres_shell->GetPresContext();
    1.83 +  if (!pres_context) {
    1.84 +    return NS_OK;
    1.85 +  }
    1.86 +
    1.87 +  nsEventStatus status = nsEventStatus_eIgnore;
    1.88 +  WidgetEvent event(true, mMessage);
    1.89 +
    1.90 +  event.mFlags.mBubbles = false;
    1.91 +  EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status);
    1.92 +  return NS_OK;
    1.93 +}
    1.94 +
    1.95 +// Fire off an event that'll asynchronously call the image elements
    1.96 +// onload handler once handled. This is needed since the image library
    1.97 +// can't decide if it wants to call it's observer methods
    1.98 +// synchronously or asynchronously. If an image is loaded from the
    1.99 +// cache the notifications come back synchronously, but if the image
   1.100 +// is loaded from the netswork the notifications come back
   1.101 +// asynchronously.
   1.102 +
   1.103 +void
   1.104 +FireImageDOMEvent(nsIContent* aContent, uint32_t aMessage)
   1.105 +{
   1.106 +  NS_ASSERTION(aMessage == NS_LOAD || aMessage == NS_LOAD_ERROR,
   1.107 +               "invalid message");
   1.108 +
   1.109 +  nsCOMPtr<nsIRunnable> event = new nsImageBoxFrameEvent(aContent, aMessage);
   1.110 +  if (NS_FAILED(NS_DispatchToCurrentThread(event)))
   1.111 +    NS_WARNING("failed to dispatch image event");
   1.112 +}
   1.113 +
   1.114 +//
   1.115 +// NS_NewImageBoxFrame
   1.116 +//
   1.117 +// Creates a new image frame and returns it
   1.118 +//
   1.119 +nsIFrame*
   1.120 +NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.121 +{
   1.122 +  return new (aPresShell) nsImageBoxFrame (aPresShell, aContext);
   1.123 +}
   1.124 +
   1.125 +NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame)
   1.126 +
   1.127 +nsresult
   1.128 +nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID,
   1.129 +                                  nsIAtom* aAttribute,
   1.130 +                                  int32_t aModType)
   1.131 +{
   1.132 +  nsresult rv = nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
   1.133 +                                                 aModType);
   1.134 +
   1.135 +  if (aAttribute == nsGkAtoms::src) {
   1.136 +    UpdateImage();
   1.137 +    PresContext()->PresShell()->
   1.138 +      FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   1.139 +  }
   1.140 +  else if (aAttribute == nsGkAtoms::validate)
   1.141 +    UpdateLoadFlags();
   1.142 +
   1.143 +  return rv;
   1.144 +}
   1.145 +
   1.146 +nsImageBoxFrame::nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext):
   1.147 +  nsLeafBoxFrame(aShell, aContext),
   1.148 +  mIntrinsicSize(0,0),
   1.149 +  mRequestRegistered(false),
   1.150 +  mLoadFlags(nsIRequest::LOAD_NORMAL),
   1.151 +  mUseSrcAttr(false),
   1.152 +  mSuppressStyleCheck(false),
   1.153 +  mFireEventOnDecode(false)
   1.154 +{
   1.155 +  MarkIntrinsicWidthsDirty();
   1.156 +}
   1.157 +
   1.158 +nsImageBoxFrame::~nsImageBoxFrame()
   1.159 +{
   1.160 +}
   1.161 +
   1.162 +
   1.163 +/* virtual */ void
   1.164 +nsImageBoxFrame::MarkIntrinsicWidthsDirty()
   1.165 +{
   1.166 +  SizeNeedsRecalc(mImageSize);
   1.167 +  nsLeafBoxFrame::MarkIntrinsicWidthsDirty();
   1.168 +}
   1.169 +
   1.170 +void
   1.171 +nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
   1.172 +{
   1.173 +  if (mImageRequest) {
   1.174 +    nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
   1.175 +                                          &mRequestRegistered);
   1.176 +
   1.177 +    // Release image loader first so that it's refcnt can go to zero
   1.178 +    mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
   1.179 +  }
   1.180 +
   1.181 +  if (mListener)
   1.182 +    reinterpret_cast<nsImageBoxListener*>(mListener.get())->SetFrame(nullptr); // set the frame to null so we don't send messages to a dead object.
   1.183 +
   1.184 +  nsLeafBoxFrame::DestroyFrom(aDestructRoot);
   1.185 +}
   1.186 +
   1.187 +
   1.188 +void
   1.189 +nsImageBoxFrame::Init(nsIContent*      aContent,
   1.190 +                      nsIFrame*        aParent,
   1.191 +                      nsIFrame*        aPrevInFlow)
   1.192 +{
   1.193 +  if (!mListener) {
   1.194 +    nsImageBoxListener *listener = new nsImageBoxListener();
   1.195 +    NS_ADDREF(listener);
   1.196 +    listener->SetFrame(this);
   1.197 +    listener->QueryInterface(NS_GET_IID(imgINotificationObserver), getter_AddRefs(mListener));
   1.198 +    NS_RELEASE(listener);
   1.199 +  }
   1.200 +
   1.201 +  mSuppressStyleCheck = true;
   1.202 +  nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
   1.203 +  mSuppressStyleCheck = false;
   1.204 +
   1.205 +  UpdateLoadFlags();
   1.206 +  UpdateImage();
   1.207 +}
   1.208 +
   1.209 +void
   1.210 +nsImageBoxFrame::UpdateImage()
   1.211 +{
   1.212 +  nsPresContext* presContext = PresContext();
   1.213 +
   1.214 +  if (mImageRequest) {
   1.215 +    nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest,
   1.216 +                                          &mRequestRegistered);
   1.217 +    mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
   1.218 +    mImageRequest = nullptr;
   1.219 +  }
   1.220 +
   1.221 +  // get the new image src
   1.222 +  nsAutoString src;
   1.223 +  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
   1.224 +  mUseSrcAttr = !src.IsEmpty();
   1.225 +  if (mUseSrcAttr) {
   1.226 +    nsIDocument* doc = mContent->GetDocument();
   1.227 +    if (!doc) {
   1.228 +      // No need to do anything here...
   1.229 +      return;
   1.230 +    }
   1.231 +    nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
   1.232 +    nsCOMPtr<nsIURI> uri;
   1.233 +    nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
   1.234 +                                              src,
   1.235 +                                              doc,
   1.236 +                                              baseURI);
   1.237 +
   1.238 +    if (uri && nsContentUtils::CanLoadImage(uri, mContent, doc,
   1.239 +                                            mContent->NodePrincipal())) {
   1.240 +      nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(),
   1.241 +                                doc->GetDocumentURI(), mListener, mLoadFlags,
   1.242 +                                EmptyString(), getter_AddRefs(mImageRequest));
   1.243 +
   1.244 +      if (mImageRequest) {
   1.245 +        nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
   1.246 +                                                      mImageRequest,
   1.247 +                                                      &mRequestRegistered);
   1.248 +      }
   1.249 +    }
   1.250 +  } else {
   1.251 +    // Only get the list-style-image if we aren't being drawn
   1.252 +    // by a native theme.
   1.253 +    uint8_t appearance = StyleDisplay()->mAppearance;
   1.254 +    if (!(appearance && nsBox::gTheme &&
   1.255 +          nsBox::gTheme->ThemeSupportsWidget(nullptr, this, appearance))) {
   1.256 +      // get the list-style-image
   1.257 +      imgRequestProxy *styleRequest = StyleList()->GetListStyleImage();
   1.258 +      if (styleRequest) {
   1.259 +        styleRequest->Clone(mListener, getter_AddRefs(mImageRequest));
   1.260 +      }
   1.261 +    }
   1.262 +  }
   1.263 +
   1.264 +  if (!mImageRequest) {
   1.265 +    // We have no image, so size to 0
   1.266 +    mIntrinsicSize.SizeTo(0, 0);
   1.267 +  } else {
   1.268 +    // We don't want discarding or decode-on-draw for xul images.
   1.269 +    mImageRequest->StartDecoding();
   1.270 +    mImageRequest->LockImage();
   1.271 +  }
   1.272 +}
   1.273 +
   1.274 +void
   1.275 +nsImageBoxFrame::UpdateLoadFlags()
   1.276 +{
   1.277 +  static nsIContent::AttrValuesArray strings[] =
   1.278 +    {&nsGkAtoms::always, &nsGkAtoms::never, nullptr};
   1.279 +  switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::validate,
   1.280 +                                    strings, eCaseMatters)) {
   1.281 +    case 0:
   1.282 +      mLoadFlags = nsIRequest::VALIDATE_ALWAYS;
   1.283 +      break;
   1.284 +    case 1:
   1.285 +      mLoadFlags = nsIRequest::VALIDATE_NEVER|nsIRequest::LOAD_FROM_CACHE;
   1.286 +      break;
   1.287 +    default:
   1.288 +      mLoadFlags = nsIRequest::LOAD_NORMAL;
   1.289 +      break;
   1.290 +  }
   1.291 +}
   1.292 +
   1.293 +void
   1.294 +nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   1.295 +                                  const nsRect&           aDirtyRect,
   1.296 +                                  const nsDisplayListSet& aLists)
   1.297 +{
   1.298 +  nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
   1.299 +
   1.300 +  if ((0 == mRect.width) || (0 == mRect.height)) {
   1.301 +    // Do not render when given a zero area. This avoids some useless
   1.302 +    // scaling work while we wait for our image dimensions to arrive
   1.303 +    // asynchronously.
   1.304 +    return;
   1.305 +  }
   1.306 +
   1.307 +  if (!IsVisibleForPainting(aBuilder))
   1.308 +    return;
   1.309 +
   1.310 +  nsDisplayList list;
   1.311 +  list.AppendNewToTop(
   1.312 +    new (aBuilder) nsDisplayXULImage(aBuilder, this));
   1.313 +
   1.314 +  CreateOwnLayerIfNeeded(aBuilder, &list);
   1.315 +
   1.316 +  aLists.Content()->AppendToTop(&list);
   1.317 +}
   1.318 +
   1.319 +void
   1.320 +nsImageBoxFrame::PaintImage(nsRenderingContext& aRenderingContext,
   1.321 +                            const nsRect& aDirtyRect, nsPoint aPt,
   1.322 +                            uint32_t aFlags)
   1.323 +{
   1.324 +  nsRect rect;
   1.325 +  GetClientRect(rect);
   1.326 +
   1.327 +  rect += aPt;
   1.328 +
   1.329 +  if (!mImageRequest)
   1.330 +    return;
   1.331 +
   1.332 +  // don't draw if the image is not dirty
   1.333 +  nsRect dirty;
   1.334 +  if (!dirty.IntersectRect(aDirtyRect, rect))
   1.335 +    return;
   1.336 +
   1.337 +  nsCOMPtr<imgIContainer> imgCon;
   1.338 +  mImageRequest->GetImage(getter_AddRefs(imgCon));
   1.339 +
   1.340 +  if (imgCon) {
   1.341 +    bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
   1.342 +    nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
   1.343 +        nsLayoutUtils::GetGraphicsFilterForFrame(this),
   1.344 +        rect, dirty, nullptr, aFlags, hasSubRect ? &mSubRect : nullptr);
   1.345 +  }
   1.346 +}
   1.347 +
   1.348 +void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
   1.349 +                              nsRenderingContext* aCtx)
   1.350 +{
   1.351 +  uint32_t flags = imgIContainer::FLAG_NONE;
   1.352 +  if (aBuilder->ShouldSyncDecodeImages())
   1.353 +    flags |= imgIContainer::FLAG_SYNC_DECODE;
   1.354 +  if (aBuilder->IsPaintingToWindow())
   1.355 +    flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
   1.356 +
   1.357 +  static_cast<nsImageBoxFrame*>(mFrame)->
   1.358 +    PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(), flags);
   1.359 +}
   1.360 +
   1.361 +void
   1.362 +nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
   1.363 +                                             const nsDisplayItemGeometry* aGeometry,
   1.364 +                                             nsRegion* aInvalidRegion)
   1.365 +{
   1.366 +
   1.367 +  if (aBuilder->ShouldSyncDecodeImages()) {
   1.368 +    nsImageBoxFrame* boxFrame = static_cast<nsImageBoxFrame*>(mFrame);
   1.369 +    nsCOMPtr<imgIContainer> image;
   1.370 +    if (boxFrame->mImageRequest) {
   1.371 +      boxFrame->mImageRequest->GetImage(getter_AddRefs(image));
   1.372 +    }
   1.373 +
   1.374 +    if  (image && !image->IsDecoded()) {
   1.375 +      bool snap;
   1.376 +      aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   1.377 +    }
   1.378 +  }
   1.379 +
   1.380 +  nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
   1.381 +}
   1.382 +
   1.383 +void
   1.384 +nsDisplayXULImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset)
   1.385 +{
   1.386 +  aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
   1.387 +
   1.388 +  int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
   1.389 +  nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
   1.390 +
   1.391 +  nsRect dest;
   1.392 +  imageFrame->GetClientRect(dest);
   1.393 +  dest += ToReferenceFrame();
   1.394 +  gfxRect destRect(dest.x, dest.y, dest.width, dest.height);
   1.395 +  destRect.ScaleInverse(factor); 
   1.396 +
   1.397 +  nsCOMPtr<imgIContainer> imgCon;
   1.398 +  imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon));
   1.399 +  int32_t imageWidth;
   1.400 +  int32_t imageHeight;
   1.401 +  imgCon->GetWidth(&imageWidth);
   1.402 +  imgCon->GetHeight(&imageHeight);
   1.403 +
   1.404 +  NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
   1.405 +
   1.406 +  gfxPoint p = destRect.TopLeft() + aOffset;
   1.407 +  gfx::Matrix transform;
   1.408 +  transform.Translate(p.x, p.y);
   1.409 +  transform.Scale(destRect.Width()/imageWidth,
   1.410 +                  destRect.Height()/imageHeight);
   1.411 +  aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
   1.412 +
   1.413 +  aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight));
   1.414 +}
   1.415 +
   1.416 +already_AddRefed<ImageContainer>
   1.417 +nsDisplayXULImage::GetContainer(LayerManager* aManager, nsDisplayListBuilder* aBuilder)
   1.418 +{
   1.419 +  return static_cast<nsImageBoxFrame*>(mFrame)->GetContainer(aManager);
   1.420 +}
   1.421 +
   1.422 +already_AddRefed<ImageContainer>
   1.423 +nsImageBoxFrame::GetContainer(LayerManager* aManager)
   1.424 +{
   1.425 +  bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
   1.426 +  if (hasSubRect || !mImageRequest) {
   1.427 +    return nullptr;
   1.428 +  }
   1.429 +
   1.430 +  nsCOMPtr<imgIContainer> imgCon;
   1.431 +  mImageRequest->GetImage(getter_AddRefs(imgCon));
   1.432 +  if (!imgCon) {
   1.433 +    return nullptr;
   1.434 +  }
   1.435 +  
   1.436 +  nsRefPtr<ImageContainer> container;
   1.437 +  nsresult rv = imgCon->GetImageContainer(aManager, getter_AddRefs(container));
   1.438 +  NS_ENSURE_SUCCESS(rv, nullptr);
   1.439 +  return container.forget();
   1.440 +}
   1.441 +
   1.442 +
   1.443 +//
   1.444 +// DidSetStyleContext
   1.445 +//
   1.446 +// When the style context changes, make sure that all of our image is up to date.
   1.447 +//
   1.448 +/* virtual */ void
   1.449 +nsImageBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
   1.450 +{
   1.451 +  nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext);
   1.452 +
   1.453 +  // Fetch our subrect.
   1.454 +  const nsStyleList* myList = StyleList();
   1.455 +  mSubRect = myList->mImageRegion; // before |mSuppressStyleCheck| test!
   1.456 +
   1.457 +  if (mUseSrcAttr || mSuppressStyleCheck)
   1.458 +    return; // No more work required, since the image isn't specified by style.
   1.459 +
   1.460 +  // If we're using a native theme implementation, we shouldn't draw anything.
   1.461 +  const nsStyleDisplay* disp = StyleDisplay();
   1.462 +  if (disp->mAppearance && nsBox::gTheme &&
   1.463 +      nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance))
   1.464 +    return;
   1.465 +
   1.466 +  // If list-style-image changes, we have a new image.
   1.467 +  nsCOMPtr<nsIURI> oldURI, newURI;
   1.468 +  if (mImageRequest)
   1.469 +    mImageRequest->GetURI(getter_AddRefs(oldURI));
   1.470 +  if (myList->GetListStyleImage())
   1.471 +    myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI));
   1.472 +  bool equal;
   1.473 +  if (newURI == oldURI ||   // handles null==null
   1.474 +      (newURI && oldURI &&
   1.475 +       NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal))
   1.476 +    return;
   1.477 +
   1.478 +  UpdateImage();
   1.479 +} // DidSetStyleContext
   1.480 +
   1.481 +void
   1.482 +nsImageBoxFrame::GetImageSize()
   1.483 +{
   1.484 +  if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
   1.485 +    mImageSize.width = mIntrinsicSize.width;
   1.486 +    mImageSize.height = mIntrinsicSize.height;
   1.487 +  } else {
   1.488 +    mImageSize.width = 0;
   1.489 +    mImageSize.height = 0;
   1.490 +  }
   1.491 +}
   1.492 +
   1.493 +/**
   1.494 + * Ok return our dimensions
   1.495 + */
   1.496 +nsSize
   1.497 +nsImageBoxFrame::GetPrefSize(nsBoxLayoutState& aState)
   1.498 +{
   1.499 +  nsSize size(0,0);
   1.500 +  DISPLAY_PREF_SIZE(this, size);
   1.501 +  if (DoesNeedRecalc(mImageSize))
   1.502 +     GetImageSize();
   1.503 +
   1.504 +  if (!mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0))
   1.505 +    size = mSubRect.Size();
   1.506 +  else
   1.507 +    size = mImageSize;
   1.508 +
   1.509 +  nsSize intrinsicSize = size;
   1.510 +
   1.511 +  nsMargin borderPadding(0,0,0,0);
   1.512 +  GetBorderAndPadding(borderPadding);
   1.513 +  size.width += borderPadding.LeftRight();
   1.514 +  size.height += borderPadding.TopBottom();
   1.515 +
   1.516 +  bool widthSet, heightSet;
   1.517 +  nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet);
   1.518 +  NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
   1.519 +               "non-intrinsic size expected");
   1.520 +
   1.521 +  nsSize minSize = GetMinSize(aState);
   1.522 +  nsSize maxSize = GetMaxSize(aState);
   1.523 +
   1.524 +  if (!widthSet && !heightSet) {
   1.525 +    if (minSize.width != NS_INTRINSICSIZE)
   1.526 +      minSize.width -= borderPadding.LeftRight();
   1.527 +    if (minSize.height != NS_INTRINSICSIZE)
   1.528 +      minSize.height -= borderPadding.TopBottom();
   1.529 +    if (maxSize.width != NS_INTRINSICSIZE)
   1.530 +      maxSize.width -= borderPadding.LeftRight();
   1.531 +    if (maxSize.height != NS_INTRINSICSIZE)
   1.532 +      maxSize.height -= borderPadding.TopBottom();
   1.533 +
   1.534 +    size = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(minSize.width, minSize.height,
   1.535 +                                                                 maxSize.width, maxSize.height,
   1.536 +                                                                 intrinsicSize.width, intrinsicSize.height);
   1.537 +    NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
   1.538 +                 "non-intrinsic size expected");
   1.539 +    size.width += borderPadding.LeftRight();
   1.540 +    size.height += borderPadding.TopBottom();
   1.541 +    return size;
   1.542 +  }
   1.543 +
   1.544 +  if (!widthSet) {
   1.545 +    if (intrinsicSize.height > 0) {
   1.546 +      // Subtract off the border and padding from the height because the
   1.547 +      // content-box needs to be used to determine the ratio
   1.548 +      nscoord height = size.height - borderPadding.TopBottom();
   1.549 +      size.width = nscoord(int64_t(height) * int64_t(intrinsicSize.width) /
   1.550 +                           int64_t(intrinsicSize.height));
   1.551 +    }
   1.552 +    else {
   1.553 +      size.width = intrinsicSize.width;
   1.554 +    }
   1.555 +
   1.556 +    size.width += borderPadding.LeftRight();
   1.557 +  }
   1.558 +  else if (!heightSet) {
   1.559 +    if (intrinsicSize.width > 0) {
   1.560 +      nscoord width = size.width - borderPadding.LeftRight();
   1.561 +      size.height = nscoord(int64_t(width) * int64_t(intrinsicSize.height) /
   1.562 +                            int64_t(intrinsicSize.width));
   1.563 +    }
   1.564 +    else {
   1.565 +      size.height = intrinsicSize.height;
   1.566 +    }
   1.567 +
   1.568 +    size.height += borderPadding.TopBottom();
   1.569 +  }
   1.570 +
   1.571 +  return BoundsCheck(minSize, size, maxSize);
   1.572 +}
   1.573 +
   1.574 +nsSize
   1.575 +nsImageBoxFrame::GetMinSize(nsBoxLayoutState& aState)
   1.576 +{
   1.577 +  // An image can always scale down to (0,0).
   1.578 +  nsSize size(0,0);
   1.579 +  DISPLAY_MIN_SIZE(this, size);
   1.580 +  AddBorderAndPadding(size);
   1.581 +  bool widthSet, heightSet;
   1.582 +  nsIFrame::AddCSSMinSize(aState, this, size, widthSet, heightSet);
   1.583 +  return size;
   1.584 +}
   1.585 +
   1.586 +nscoord
   1.587 +nsImageBoxFrame::GetBoxAscent(nsBoxLayoutState& aState)
   1.588 +{
   1.589 +  return GetPrefSize(aState).height;
   1.590 +}
   1.591 +
   1.592 +nsIAtom*
   1.593 +nsImageBoxFrame::GetType() const
   1.594 +{
   1.595 +  return nsGkAtoms::imageBoxFrame;
   1.596 +}
   1.597 +
   1.598 +#ifdef DEBUG_FRAME_DUMP
   1.599 +nsresult
   1.600 +nsImageBoxFrame::GetFrameName(nsAString& aResult) const
   1.601 +{
   1.602 +  return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult);
   1.603 +}
   1.604 +#endif
   1.605 +
   1.606 +nsresult
   1.607 +nsImageBoxFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
   1.608 +{
   1.609 +  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
   1.610 +    nsCOMPtr<imgIContainer> image;
   1.611 +    aRequest->GetImage(getter_AddRefs(image));
   1.612 +    return OnStartContainer(aRequest, image);
   1.613 +  }
   1.614 +
   1.615 +  if (aType == imgINotificationObserver::DECODE_COMPLETE) {
   1.616 +    return OnStopDecode(aRequest);
   1.617 +  }
   1.618 +
   1.619 +  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
   1.620 +    uint32_t imgStatus;
   1.621 +    aRequest->GetImageStatus(&imgStatus);
   1.622 +    nsresult status =
   1.623 +        imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
   1.624 +    return OnStopRequest(aRequest, status);
   1.625 +  }
   1.626 +
   1.627 +  if (aType == imgINotificationObserver::IS_ANIMATED) {
   1.628 +    return OnImageIsAnimated(aRequest);
   1.629 +  }
   1.630 +
   1.631 +  if (aType == imgINotificationObserver::FRAME_UPDATE) {
   1.632 +    return FrameChanged(aRequest);
   1.633 +  }
   1.634 +
   1.635 +  return NS_OK;
   1.636 +}
   1.637 +
   1.638 +nsresult nsImageBoxFrame::OnStartContainer(imgIRequest *request,
   1.639 +                                           imgIContainer *image)
   1.640 +{
   1.641 +  NS_ENSURE_ARG_POINTER(image);
   1.642 +
   1.643 +  // Ensure the animation (if any) is started. Note: There is no
   1.644 +  // corresponding call to Decrement for this. This Increment will be
   1.645 +  // 'cleaned up' by the Request when it is destroyed, but only then.
   1.646 +  request->IncrementAnimationConsumers();
   1.647 +
   1.648 +  nscoord w, h;
   1.649 +  image->GetWidth(&w);
   1.650 +  image->GetHeight(&h);
   1.651 +
   1.652 +  mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w),
   1.653 +                        nsPresContext::CSSPixelsToAppUnits(h));
   1.654 +
   1.655 +  if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
   1.656 +    PresContext()->PresShell()->
   1.657 +      FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   1.658 +  }
   1.659 +
   1.660 +  return NS_OK;
   1.661 +}
   1.662 +
   1.663 +nsresult nsImageBoxFrame::OnStopDecode(imgIRequest *request)
   1.664 +{
   1.665 +  if (mFireEventOnDecode) {
   1.666 +    mFireEventOnDecode = false;
   1.667 +
   1.668 +    uint32_t reqStatus;
   1.669 +    request->GetImageStatus(&reqStatus);
   1.670 +    if (!(reqStatus & imgIRequest::STATUS_ERROR)) {
   1.671 +      FireImageDOMEvent(mContent, NS_LOAD);
   1.672 +    } else {
   1.673 +      // Fire an onerror DOM event.
   1.674 +      mIntrinsicSize.SizeTo(0, 0);
   1.675 +      PresContext()->PresShell()->
   1.676 +        FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   1.677 +      FireImageDOMEvent(mContent, NS_LOAD_ERROR);
   1.678 +    }
   1.679 +  }
   1.680 +
   1.681 +  nsBoxLayoutState state(PresContext());
   1.682 +  this->Redraw(state);
   1.683 +
   1.684 +  return NS_OK;
   1.685 +}
   1.686 +
   1.687 +nsresult nsImageBoxFrame::OnStopRequest(imgIRequest *request,
   1.688 +                                        nsresult aStatus)
   1.689 +{
   1.690 +  uint32_t reqStatus;
   1.691 +  request->GetImageStatus(&reqStatus);
   1.692 +
   1.693 +  // We want to give the decoder a chance to find errors. If we haven't found
   1.694 +  // an error yet and we've already started decoding, we must only fire these
   1.695 +  // events after we finish decoding.
   1.696 +  if (NS_SUCCEEDED(aStatus) && !(reqStatus & imgIRequest::STATUS_ERROR) &&
   1.697 +      reqStatus & imgIRequest::STATUS_DECODE_STARTED) {
   1.698 +    mFireEventOnDecode = true;
   1.699 +  } else {
   1.700 +    if (NS_SUCCEEDED(aStatus)) {
   1.701 +      // Fire an onload DOM event.
   1.702 +      FireImageDOMEvent(mContent, NS_LOAD);
   1.703 +    } else {
   1.704 +      // Fire an onerror DOM event.
   1.705 +      mIntrinsicSize.SizeTo(0, 0);
   1.706 +      PresContext()->PresShell()->
   1.707 +        FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   1.708 +      FireImageDOMEvent(mContent, NS_LOAD_ERROR);
   1.709 +    }
   1.710 +  }
   1.711 +
   1.712 +  return NS_OK;
   1.713 +}
   1.714 +
   1.715 +nsresult nsImageBoxFrame::OnImageIsAnimated(imgIRequest *aRequest)
   1.716 +{
   1.717 +  // Register with our refresh driver, if we're animated.
   1.718 +  nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest,
   1.719 +                                      &mRequestRegistered);
   1.720 +
   1.721 +  return NS_OK;
   1.722 +}
   1.723 +
   1.724 +nsresult nsImageBoxFrame::FrameChanged(imgIRequest *aRequest)
   1.725 +{
   1.726 +  if ((0 == mRect.width) || (0 == mRect.height)) {
   1.727 +    return NS_OK;
   1.728 +  }
   1.729 + 
   1.730 +  InvalidateLayer(nsDisplayItem::TYPE_XUL_IMAGE);
   1.731 +
   1.732 +  return NS_OK;
   1.733 +}
   1.734 +
   1.735 +NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver, imgIOnloadBlocker)
   1.736 +
   1.737 +nsImageBoxListener::nsImageBoxListener()
   1.738 +{
   1.739 +}
   1.740 +
   1.741 +nsImageBoxListener::~nsImageBoxListener()
   1.742 +{
   1.743 +}
   1.744 +
   1.745 +NS_IMETHODIMP
   1.746 +nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData)
   1.747 +{
   1.748 +  if (!mFrame)
   1.749 +    return NS_OK;
   1.750 +
   1.751 +  return mFrame->Notify(request, aType, aData);
   1.752 +}
   1.753 +
   1.754 +/* void blockOnload (in imgIRequest aRequest); */
   1.755 +NS_IMETHODIMP
   1.756 +nsImageBoxListener::BlockOnload(imgIRequest *aRequest)
   1.757 +{
   1.758 +  if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) {
   1.759 +    mFrame->GetContent()->GetCurrentDoc()->BlockOnload();
   1.760 +  }
   1.761 +
   1.762 +  return NS_OK;
   1.763 +}
   1.764 +
   1.765 +/* void unblockOnload (in imgIRequest aRequest); */
   1.766 +NS_IMETHODIMP
   1.767 +nsImageBoxListener::UnblockOnload(imgIRequest *aRequest)
   1.768 +{
   1.769 +  if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) {
   1.770 +    mFrame->GetContent()->GetCurrentDoc()->UnblockOnload(false);
   1.771 +  }
   1.772 +
   1.773 +  return NS_OK;
   1.774 +}

mercurial