michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // michael@0: // Eric Vaughan michael@0: // Netscape Communications michael@0: // michael@0: // See documentation in associated header file michael@0: // michael@0: michael@0: #include "nsImageBoxFrame.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsBoxLayoutState.h" michael@0: michael@0: #include "nsHTMLParts.h" michael@0: #include "nsString.h" michael@0: #include "nsLeafFrame.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsImageMap.h" michael@0: #include "nsILinkHandler.h" michael@0: #include "nsIURL.h" michael@0: #include "nsILoadGroup.h" michael@0: #include "nsContainerFrame.h" michael@0: #include "prprf.h" michael@0: #include "nsCSSRendering.h" michael@0: #include "nsIDOMHTMLImageElement.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsTextFragment.h" michael@0: #include "nsIDOMHTMLMapElement.h" michael@0: #include "nsTransform2D.h" michael@0: #include "nsITheme.h" michael@0: michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIURI.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsDisplayList.h" michael@0: #include "ImageLayers.h" michael@0: #include "ImageContainer.h" michael@0: michael@0: #include "nsContentUtils.h" michael@0: michael@0: #include "mozilla/BasicEvents.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: michael@0: #define ONLOAD_CALLED_TOO_EARLY 1 michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::layers; michael@0: michael@0: class nsImageBoxFrameEvent : public nsRunnable michael@0: { michael@0: public: michael@0: nsImageBoxFrameEvent(nsIContent *content, uint32_t message) michael@0: : mContent(content), mMessage(message) {} michael@0: michael@0: NS_IMETHOD Run() MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: nsCOMPtr mContent; michael@0: uint32_t mMessage; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: nsImageBoxFrameEvent::Run() michael@0: { michael@0: nsIPresShell *pres_shell = mContent->OwnerDoc()->GetShell(); michael@0: if (!pres_shell) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr pres_context = pres_shell->GetPresContext(); michael@0: if (!pres_context) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: WidgetEvent event(true, mMessage); michael@0: michael@0: event.mFlags.mBubbles = false; michael@0: EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Fire off an event that'll asynchronously call the image elements michael@0: // onload handler once handled. This is needed since the image library michael@0: // can't decide if it wants to call it's observer methods michael@0: // synchronously or asynchronously. If an image is loaded from the michael@0: // cache the notifications come back synchronously, but if the image michael@0: // is loaded from the netswork the notifications come back michael@0: // asynchronously. michael@0: michael@0: void michael@0: FireImageDOMEvent(nsIContent* aContent, uint32_t aMessage) michael@0: { michael@0: NS_ASSERTION(aMessage == NS_LOAD || aMessage == NS_LOAD_ERROR, michael@0: "invalid message"); michael@0: michael@0: nsCOMPtr event = new nsImageBoxFrameEvent(aContent, aMessage); michael@0: if (NS_FAILED(NS_DispatchToCurrentThread(event))) michael@0: NS_WARNING("failed to dispatch image event"); michael@0: } michael@0: michael@0: // michael@0: // NS_NewImageBoxFrame michael@0: // michael@0: // Creates a new image frame and returns it michael@0: // michael@0: nsIFrame* michael@0: NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsImageBoxFrame (aPresShell, aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame) michael@0: michael@0: nsresult michael@0: nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: nsresult rv = nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, michael@0: aModType); michael@0: michael@0: if (aAttribute == nsGkAtoms::src) { michael@0: UpdateImage(); michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); michael@0: } michael@0: else if (aAttribute == nsGkAtoms::validate) michael@0: UpdateLoadFlags(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsImageBoxFrame::nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext): michael@0: nsLeafBoxFrame(aShell, aContext), michael@0: mIntrinsicSize(0,0), michael@0: mRequestRegistered(false), michael@0: mLoadFlags(nsIRequest::LOAD_NORMAL), michael@0: mUseSrcAttr(false), michael@0: mSuppressStyleCheck(false), michael@0: mFireEventOnDecode(false) michael@0: { michael@0: MarkIntrinsicWidthsDirty(); michael@0: } michael@0: michael@0: nsImageBoxFrame::~nsImageBoxFrame() michael@0: { michael@0: } michael@0: michael@0: michael@0: /* virtual */ void michael@0: nsImageBoxFrame::MarkIntrinsicWidthsDirty() michael@0: { michael@0: SizeNeedsRecalc(mImageSize); michael@0: nsLeafBoxFrame::MarkIntrinsicWidthsDirty(); michael@0: } michael@0: michael@0: void michael@0: nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: if (mImageRequest) { michael@0: nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest, michael@0: &mRequestRegistered); michael@0: michael@0: // Release image loader first so that it's refcnt can go to zero michael@0: mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); michael@0: } michael@0: michael@0: if (mListener) michael@0: reinterpret_cast(mListener.get())->SetFrame(nullptr); // set the frame to null so we don't send messages to a dead object. michael@0: michael@0: nsLeafBoxFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: michael@0: void michael@0: nsImageBoxFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: if (!mListener) { michael@0: nsImageBoxListener *listener = new nsImageBoxListener(); michael@0: NS_ADDREF(listener); michael@0: listener->SetFrame(this); michael@0: listener->QueryInterface(NS_GET_IID(imgINotificationObserver), getter_AddRefs(mListener)); michael@0: NS_RELEASE(listener); michael@0: } michael@0: michael@0: mSuppressStyleCheck = true; michael@0: nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow); michael@0: mSuppressStyleCheck = false; michael@0: michael@0: UpdateLoadFlags(); michael@0: UpdateImage(); michael@0: } michael@0: michael@0: void michael@0: nsImageBoxFrame::UpdateImage() michael@0: { michael@0: nsPresContext* presContext = PresContext(); michael@0: michael@0: if (mImageRequest) { michael@0: nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest, michael@0: &mRequestRegistered); michael@0: mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); michael@0: mImageRequest = nullptr; michael@0: } michael@0: michael@0: // get the new image src michael@0: nsAutoString src; michael@0: mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); michael@0: mUseSrcAttr = !src.IsEmpty(); michael@0: if (mUseSrcAttr) { michael@0: nsIDocument* doc = mContent->GetDocument(); michael@0: if (!doc) { michael@0: // No need to do anything here... michael@0: return; michael@0: } michael@0: nsCOMPtr baseURI = mContent->GetBaseURI(); michael@0: nsCOMPtr uri; michael@0: nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), michael@0: src, michael@0: doc, michael@0: baseURI); michael@0: michael@0: if (uri && nsContentUtils::CanLoadImage(uri, mContent, doc, michael@0: mContent->NodePrincipal())) { michael@0: nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(), michael@0: doc->GetDocumentURI(), mListener, mLoadFlags, michael@0: EmptyString(), getter_AddRefs(mImageRequest)); michael@0: michael@0: if (mImageRequest) { michael@0: nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, michael@0: mImageRequest, michael@0: &mRequestRegistered); michael@0: } michael@0: } michael@0: } else { michael@0: // Only get the list-style-image if we aren't being drawn michael@0: // by a native theme. michael@0: uint8_t appearance = StyleDisplay()->mAppearance; michael@0: if (!(appearance && nsBox::gTheme && michael@0: nsBox::gTheme->ThemeSupportsWidget(nullptr, this, appearance))) { michael@0: // get the list-style-image michael@0: imgRequestProxy *styleRequest = StyleList()->GetListStyleImage(); michael@0: if (styleRequest) { michael@0: styleRequest->Clone(mListener, getter_AddRefs(mImageRequest)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!mImageRequest) { michael@0: // We have no image, so size to 0 michael@0: mIntrinsicSize.SizeTo(0, 0); michael@0: } else { michael@0: // We don't want discarding or decode-on-draw for xul images. michael@0: mImageRequest->StartDecoding(); michael@0: mImageRequest->LockImage(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsImageBoxFrame::UpdateLoadFlags() michael@0: { michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::always, &nsGkAtoms::never, nullptr}; michael@0: switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::validate, michael@0: strings, eCaseMatters)) { michael@0: case 0: michael@0: mLoadFlags = nsIRequest::VALIDATE_ALWAYS; michael@0: break; michael@0: case 1: michael@0: mLoadFlags = nsIRequest::VALIDATE_NEVER|nsIRequest::LOAD_FROM_CACHE; michael@0: break; michael@0: default: michael@0: mLoadFlags = nsIRequest::LOAD_NORMAL; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); michael@0: michael@0: if ((0 == mRect.width) || (0 == mRect.height)) { michael@0: // Do not render when given a zero area. This avoids some useless michael@0: // scaling work while we wait for our image dimensions to arrive michael@0: // asynchronously. michael@0: return; michael@0: } michael@0: michael@0: if (!IsVisibleForPainting(aBuilder)) michael@0: return; michael@0: michael@0: nsDisplayList list; michael@0: list.AppendNewToTop( michael@0: new (aBuilder) nsDisplayXULImage(aBuilder, this)); michael@0: michael@0: CreateOwnLayerIfNeeded(aBuilder, &list); michael@0: michael@0: aLists.Content()->AppendToTop(&list); michael@0: } michael@0: michael@0: void michael@0: nsImageBoxFrame::PaintImage(nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, nsPoint aPt, michael@0: uint32_t aFlags) michael@0: { michael@0: nsRect rect; michael@0: GetClientRect(rect); michael@0: michael@0: rect += aPt; michael@0: michael@0: if (!mImageRequest) michael@0: return; michael@0: michael@0: // don't draw if the image is not dirty michael@0: nsRect dirty; michael@0: if (!dirty.IntersectRect(aDirtyRect, rect)) michael@0: return; michael@0: michael@0: nsCOMPtr imgCon; michael@0: mImageRequest->GetImage(getter_AddRefs(imgCon)); michael@0: michael@0: if (imgCon) { michael@0: bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0); michael@0: nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon, michael@0: nsLayoutUtils::GetGraphicsFilterForFrame(this), michael@0: rect, dirty, nullptr, aFlags, hasSubRect ? &mSubRect : nullptr); michael@0: } michael@0: } michael@0: michael@0: void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: uint32_t flags = imgIContainer::FLAG_NONE; michael@0: if (aBuilder->ShouldSyncDecodeImages()) michael@0: flags |= imgIContainer::FLAG_SYNC_DECODE; michael@0: if (aBuilder->IsPaintingToWindow()) michael@0: flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING; michael@0: michael@0: static_cast(mFrame)-> michael@0: PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(), flags); michael@0: } michael@0: michael@0: void michael@0: nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion* aInvalidRegion) michael@0: { michael@0: michael@0: if (aBuilder->ShouldSyncDecodeImages()) { michael@0: nsImageBoxFrame* boxFrame = static_cast(mFrame); michael@0: nsCOMPtr image; michael@0: if (boxFrame->mImageRequest) { michael@0: boxFrame->mImageRequest->GetImage(getter_AddRefs(image)); michael@0: } michael@0: michael@0: if (image && !image->IsDecoded()) { michael@0: bool snap; michael@0: aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); michael@0: } michael@0: } michael@0: michael@0: nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); michael@0: } michael@0: michael@0: void michael@0: nsDisplayXULImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset) michael@0: { michael@0: aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame)); michael@0: michael@0: int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: nsImageBoxFrame* imageFrame = static_cast(mFrame); michael@0: michael@0: nsRect dest; michael@0: imageFrame->GetClientRect(dest); michael@0: dest += ToReferenceFrame(); michael@0: gfxRect destRect(dest.x, dest.y, dest.width, dest.height); michael@0: destRect.ScaleInverse(factor); michael@0: michael@0: nsCOMPtr imgCon; michael@0: imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon)); michael@0: int32_t imageWidth; michael@0: int32_t imageHeight; michael@0: imgCon->GetWidth(&imageWidth); michael@0: imgCon->GetHeight(&imageHeight); michael@0: michael@0: NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!"); michael@0: michael@0: gfxPoint p = destRect.TopLeft() + aOffset; michael@0: gfx::Matrix transform; michael@0: transform.Translate(p.x, p.y); michael@0: transform.Scale(destRect.Width()/imageWidth, michael@0: destRect.Height()/imageHeight); michael@0: aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform)); michael@0: michael@0: aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight)); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDisplayXULImage::GetContainer(LayerManager* aManager, nsDisplayListBuilder* aBuilder) michael@0: { michael@0: return static_cast(mFrame)->GetContainer(aManager); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsImageBoxFrame::GetContainer(LayerManager* aManager) michael@0: { michael@0: bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0); michael@0: if (hasSubRect || !mImageRequest) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr imgCon; michael@0: mImageRequest->GetImage(getter_AddRefs(imgCon)); michael@0: if (!imgCon) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr container; michael@0: nsresult rv = imgCon->GetImageContainer(aManager, getter_AddRefs(container)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: return container.forget(); michael@0: } michael@0: michael@0: michael@0: // michael@0: // DidSetStyleContext michael@0: // michael@0: // When the style context changes, make sure that all of our image is up to date. michael@0: // michael@0: /* virtual */ void michael@0: nsImageBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) michael@0: { michael@0: nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext); michael@0: michael@0: // Fetch our subrect. michael@0: const nsStyleList* myList = StyleList(); michael@0: mSubRect = myList->mImageRegion; // before |mSuppressStyleCheck| test! michael@0: michael@0: if (mUseSrcAttr || mSuppressStyleCheck) michael@0: return; // No more work required, since the image isn't specified by style. michael@0: michael@0: // If we're using a native theme implementation, we shouldn't draw anything. michael@0: const nsStyleDisplay* disp = StyleDisplay(); michael@0: if (disp->mAppearance && nsBox::gTheme && michael@0: nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance)) michael@0: return; michael@0: michael@0: // If list-style-image changes, we have a new image. michael@0: nsCOMPtr oldURI, newURI; michael@0: if (mImageRequest) michael@0: mImageRequest->GetURI(getter_AddRefs(oldURI)); michael@0: if (myList->GetListStyleImage()) michael@0: myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI)); michael@0: bool equal; michael@0: if (newURI == oldURI || // handles null==null michael@0: (newURI && oldURI && michael@0: NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal)) michael@0: return; michael@0: michael@0: UpdateImage(); michael@0: } // DidSetStyleContext michael@0: michael@0: void michael@0: nsImageBoxFrame::GetImageSize() michael@0: { michael@0: if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) { michael@0: mImageSize.width = mIntrinsicSize.width; michael@0: mImageSize.height = mIntrinsicSize.height; michael@0: } else { michael@0: mImageSize.width = 0; michael@0: mImageSize.height = 0; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Ok return our dimensions michael@0: */ michael@0: nsSize michael@0: nsImageBoxFrame::GetPrefSize(nsBoxLayoutState& aState) michael@0: { michael@0: nsSize size(0,0); michael@0: DISPLAY_PREF_SIZE(this, size); michael@0: if (DoesNeedRecalc(mImageSize)) michael@0: GetImageSize(); michael@0: michael@0: if (!mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0)) michael@0: size = mSubRect.Size(); michael@0: else michael@0: size = mImageSize; michael@0: michael@0: nsSize intrinsicSize = size; michael@0: michael@0: nsMargin borderPadding(0,0,0,0); michael@0: GetBorderAndPadding(borderPadding); michael@0: size.width += borderPadding.LeftRight(); michael@0: size.height += borderPadding.TopBottom(); michael@0: michael@0: bool widthSet, heightSet; michael@0: nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet); michael@0: NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE, michael@0: "non-intrinsic size expected"); michael@0: michael@0: nsSize minSize = GetMinSize(aState); michael@0: nsSize maxSize = GetMaxSize(aState); michael@0: michael@0: if (!widthSet && !heightSet) { michael@0: if (minSize.width != NS_INTRINSICSIZE) michael@0: minSize.width -= borderPadding.LeftRight(); michael@0: if (minSize.height != NS_INTRINSICSIZE) michael@0: minSize.height -= borderPadding.TopBottom(); michael@0: if (maxSize.width != NS_INTRINSICSIZE) michael@0: maxSize.width -= borderPadding.LeftRight(); michael@0: if (maxSize.height != NS_INTRINSICSIZE) michael@0: maxSize.height -= borderPadding.TopBottom(); michael@0: michael@0: size = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(minSize.width, minSize.height, michael@0: maxSize.width, maxSize.height, michael@0: intrinsicSize.width, intrinsicSize.height); michael@0: NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE, michael@0: "non-intrinsic size expected"); michael@0: size.width += borderPadding.LeftRight(); michael@0: size.height += borderPadding.TopBottom(); michael@0: return size; michael@0: } michael@0: michael@0: if (!widthSet) { michael@0: if (intrinsicSize.height > 0) { michael@0: // Subtract off the border and padding from the height because the michael@0: // content-box needs to be used to determine the ratio michael@0: nscoord height = size.height - borderPadding.TopBottom(); michael@0: size.width = nscoord(int64_t(height) * int64_t(intrinsicSize.width) / michael@0: int64_t(intrinsicSize.height)); michael@0: } michael@0: else { michael@0: size.width = intrinsicSize.width; michael@0: } michael@0: michael@0: size.width += borderPadding.LeftRight(); michael@0: } michael@0: else if (!heightSet) { michael@0: if (intrinsicSize.width > 0) { michael@0: nscoord width = size.width - borderPadding.LeftRight(); michael@0: size.height = nscoord(int64_t(width) * int64_t(intrinsicSize.height) / michael@0: int64_t(intrinsicSize.width)); michael@0: } michael@0: else { michael@0: size.height = intrinsicSize.height; michael@0: } michael@0: michael@0: size.height += borderPadding.TopBottom(); michael@0: } michael@0: michael@0: return BoundsCheck(minSize, size, maxSize); michael@0: } michael@0: michael@0: nsSize michael@0: nsImageBoxFrame::GetMinSize(nsBoxLayoutState& aState) michael@0: { michael@0: // An image can always scale down to (0,0). michael@0: nsSize size(0,0); michael@0: DISPLAY_MIN_SIZE(this, size); michael@0: AddBorderAndPadding(size); michael@0: bool widthSet, heightSet; michael@0: nsIFrame::AddCSSMinSize(aState, this, size, widthSet, heightSet); michael@0: return size; michael@0: } michael@0: michael@0: nscoord michael@0: nsImageBoxFrame::GetBoxAscent(nsBoxLayoutState& aState) michael@0: { michael@0: return GetPrefSize(aState).height; michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsImageBoxFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::imageBoxFrame; michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsImageBoxFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: nsresult michael@0: nsImageBoxFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) michael@0: { michael@0: if (aType == imgINotificationObserver::SIZE_AVAILABLE) { michael@0: nsCOMPtr image; michael@0: aRequest->GetImage(getter_AddRefs(image)); michael@0: return OnStartContainer(aRequest, image); michael@0: } michael@0: michael@0: if (aType == imgINotificationObserver::DECODE_COMPLETE) { michael@0: return OnStopDecode(aRequest); michael@0: } michael@0: michael@0: if (aType == imgINotificationObserver::LOAD_COMPLETE) { michael@0: uint32_t imgStatus; michael@0: aRequest->GetImageStatus(&imgStatus); michael@0: nsresult status = michael@0: imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; michael@0: return OnStopRequest(aRequest, status); michael@0: } michael@0: michael@0: if (aType == imgINotificationObserver::IS_ANIMATED) { michael@0: return OnImageIsAnimated(aRequest); michael@0: } michael@0: michael@0: if (aType == imgINotificationObserver::FRAME_UPDATE) { michael@0: return FrameChanged(aRequest); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsImageBoxFrame::OnStartContainer(imgIRequest *request, michael@0: imgIContainer *image) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(image); michael@0: michael@0: // Ensure the animation (if any) is started. Note: There is no michael@0: // corresponding call to Decrement for this. This Increment will be michael@0: // 'cleaned up' by the Request when it is destroyed, but only then. michael@0: request->IncrementAnimationConsumers(); michael@0: michael@0: nscoord w, h; michael@0: image->GetWidth(&w); michael@0: image->GetHeight(&h); michael@0: michael@0: mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w), michael@0: nsPresContext::CSSPixelsToAppUnits(h)); michael@0: michael@0: if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsImageBoxFrame::OnStopDecode(imgIRequest *request) michael@0: { michael@0: if (mFireEventOnDecode) { michael@0: mFireEventOnDecode = false; michael@0: michael@0: uint32_t reqStatus; michael@0: request->GetImageStatus(&reqStatus); michael@0: if (!(reqStatus & imgIRequest::STATUS_ERROR)) { michael@0: FireImageDOMEvent(mContent, NS_LOAD); michael@0: } else { michael@0: // Fire an onerror DOM event. michael@0: mIntrinsicSize.SizeTo(0, 0); michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); michael@0: FireImageDOMEvent(mContent, NS_LOAD_ERROR); michael@0: } michael@0: } michael@0: michael@0: nsBoxLayoutState state(PresContext()); michael@0: this->Redraw(state); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsImageBoxFrame::OnStopRequest(imgIRequest *request, michael@0: nsresult aStatus) michael@0: { michael@0: uint32_t reqStatus; michael@0: request->GetImageStatus(&reqStatus); michael@0: michael@0: // We want to give the decoder a chance to find errors. If we haven't found michael@0: // an error yet and we've already started decoding, we must only fire these michael@0: // events after we finish decoding. michael@0: if (NS_SUCCEEDED(aStatus) && !(reqStatus & imgIRequest::STATUS_ERROR) && michael@0: reqStatus & imgIRequest::STATUS_DECODE_STARTED) { michael@0: mFireEventOnDecode = true; michael@0: } else { michael@0: if (NS_SUCCEEDED(aStatus)) { michael@0: // Fire an onload DOM event. michael@0: FireImageDOMEvent(mContent, NS_LOAD); michael@0: } else { michael@0: // Fire an onerror DOM event. michael@0: mIntrinsicSize.SizeTo(0, 0); michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); michael@0: FireImageDOMEvent(mContent, NS_LOAD_ERROR); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsImageBoxFrame::OnImageIsAnimated(imgIRequest *aRequest) michael@0: { michael@0: // Register with our refresh driver, if we're animated. michael@0: nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest, michael@0: &mRequestRegistered); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsImageBoxFrame::FrameChanged(imgIRequest *aRequest) michael@0: { michael@0: if ((0 == mRect.width) || (0 == mRect.height)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: InvalidateLayer(nsDisplayItem::TYPE_XUL_IMAGE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver, imgIOnloadBlocker) michael@0: michael@0: nsImageBoxListener::nsImageBoxListener() michael@0: { michael@0: } michael@0: michael@0: nsImageBoxListener::~nsImageBoxListener() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData) michael@0: { michael@0: if (!mFrame) michael@0: return NS_OK; michael@0: michael@0: return mFrame->Notify(request, aType, aData); michael@0: } michael@0: michael@0: /* void blockOnload (in imgIRequest aRequest); */ michael@0: NS_IMETHODIMP michael@0: nsImageBoxListener::BlockOnload(imgIRequest *aRequest) michael@0: { michael@0: if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) { michael@0: mFrame->GetContent()->GetCurrentDoc()->BlockOnload(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void unblockOnload (in imgIRequest aRequest); */ michael@0: NS_IMETHODIMP michael@0: nsImageBoxListener::UnblockOnload(imgIRequest *aRequest) michael@0: { michael@0: if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) { michael@0: mFrame->GetContent()->GetCurrentDoc()->UnblockOnload(false); michael@0: } michael@0: michael@0: return NS_OK; michael@0: }