diff options
Diffstat (limited to 'layout/xul/nsImageBoxFrame.cpp')
-rw-r--r-- | layout/xul/nsImageBoxFrame.cpp | 828 |
1 files changed, 828 insertions, 0 deletions
diff --git a/layout/xul/nsImageBoxFrame.cpp b/layout/xul/nsImageBoxFrame.cpp new file mode 100644 index 000000000..fd7c7becf --- /dev/null +++ b/layout/xul/nsImageBoxFrame.cpp @@ -0,0 +1,828 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// +// Eric Vaughan +// Netscape Communications +// +// See documentation in associated header file +// + +#include "nsImageBoxFrame.h" +#include "nsGkAtoms.h" +#include "nsRenderingContext.h" +#include "nsStyleContext.h" +#include "nsStyleConsts.h" +#include "nsStyleUtil.h" +#include "nsCOMPtr.h" +#include "nsPresContext.h" +#include "nsBoxLayoutState.h" + +#include "nsHTMLParts.h" +#include "nsString.h" +#include "nsLeafFrame.h" +#include "nsIPresShell.h" +#include "nsIDocument.h" +#include "nsImageMap.h" +#include "nsILinkHandler.h" +#include "nsIURL.h" +#include "nsILoadGroup.h" +#include "nsContainerFrame.h" +#include "prprf.h" +#include "nsCSSRendering.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsNameSpaceManager.h" +#include "nsTextFragment.h" +#include "nsIDOMHTMLMapElement.h" +#include "nsTransform2D.h" +#include "nsITheme.h" + +#include "nsIServiceManager.h" +#include "nsIURI.h" +#include "nsThreadUtils.h" +#include "nsDisplayList.h" +#include "ImageLayers.h" +#include "ImageContainer.h" +#include "nsIContent.h" + +#include "nsContentUtils.h" +#include "nsSerializationHelper.h" + +#include "mozilla/BasicEvents.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/Maybe.h" + +#define ONLOAD_CALLED_TOO_EARLY 1 + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::image; +using namespace mozilla::layers; + +class nsImageBoxFrameEvent : public Runnable +{ +public: + nsImageBoxFrameEvent(nsIContent *content, EventMessage message) + : mContent(content), mMessage(message) {} + + NS_IMETHOD Run() override; + +private: + nsCOMPtr<nsIContent> mContent; + EventMessage mMessage; +}; + +NS_IMETHODIMP +nsImageBoxFrameEvent::Run() +{ + nsIPresShell *pres_shell = mContent->OwnerDoc()->GetShell(); + if (!pres_shell) { + return NS_OK; + } + + RefPtr<nsPresContext> pres_context = pres_shell->GetPresContext(); + if (!pres_context) { + return NS_OK; + } + + nsEventStatus status = nsEventStatus_eIgnore; + WidgetEvent event(true, mMessage); + + event.mFlags.mBubbles = false; + EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status); + return NS_OK; +} + +// Fire off an event that'll asynchronously call the image elements +// onload handler once handled. This is needed since the image library +// can't decide if it wants to call it's observer methods +// synchronously or asynchronously. If an image is loaded from the +// cache the notifications come back synchronously, but if the image +// is loaded from the netswork the notifications come back +// asynchronously. + +void +FireImageDOMEvent(nsIContent* aContent, EventMessage aMessage) +{ + NS_ASSERTION(aMessage == eLoad || aMessage == eLoadError, + "invalid message"); + + nsCOMPtr<nsIRunnable> event = new nsImageBoxFrameEvent(aContent, aMessage); + if (NS_FAILED(NS_DispatchToCurrentThread(event))) + NS_WARNING("failed to dispatch image event"); +} + +// +// NS_NewImageBoxFrame +// +// Creates a new image frame and returns it +// +nsIFrame* +NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) +{ + return new (aPresShell) nsImageBoxFrame(aContext); +} + +NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame) + +nsresult +nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType) +{ + nsresult rv = nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, + aModType); + + if (aAttribute == nsGkAtoms::src) { + UpdateImage(); + PresContext()->PresShell()-> + FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); + } + else if (aAttribute == nsGkAtoms::validate) + UpdateLoadFlags(); + + return rv; +} + +nsImageBoxFrame::nsImageBoxFrame(nsStyleContext* aContext): + nsLeafBoxFrame(aContext), + mIntrinsicSize(0,0), + mLoadFlags(nsIRequest::LOAD_NORMAL), + mRequestRegistered(false), + mUseSrcAttr(false), + mSuppressStyleCheck(false) +{ + MarkIntrinsicISizesDirty(); +} + +nsImageBoxFrame::~nsImageBoxFrame() +{ +} + + +/* virtual */ void +nsImageBoxFrame::MarkIntrinsicISizesDirty() +{ + SizeNeedsRecalc(mImageSize); + nsLeafBoxFrame::MarkIntrinsicISizesDirty(); +} + +void +nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot) +{ + if (mImageRequest) { + nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest, + &mRequestRegistered); + + // Release image loader first so that it's refcnt can go to zero + mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); + } + + if (mListener) + reinterpret_cast<nsImageBoxListener*>(mListener.get())->SetFrame(nullptr); // set the frame to null so we don't send messages to a dead object. + + nsLeafBoxFrame::DestroyFrom(aDestructRoot); +} + + +void +nsImageBoxFrame::Init(nsIContent* aContent, + nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) +{ + if (!mListener) { + RefPtr<nsImageBoxListener> listener = new nsImageBoxListener(); + listener->SetFrame(this); + mListener = listener.forget(); + } + + mSuppressStyleCheck = true; + nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow); + mSuppressStyleCheck = false; + + UpdateLoadFlags(); + UpdateImage(); +} + +void +nsImageBoxFrame::UpdateImage() +{ + nsPresContext* presContext = PresContext(); + + RefPtr<imgRequestProxy> oldImageRequest = mImageRequest; + + if (mImageRequest) { + nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest, + &mRequestRegistered); + mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); + mImageRequest = nullptr; + } + + // get the new image src + nsAutoString src; + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); + mUseSrcAttr = !src.IsEmpty(); + if (mUseSrcAttr) { + nsIDocument* doc = mContent->GetComposedDoc(); + if (doc) { + // Use the serialized loadingPrincipal from the image element. Fall back + // to mContent's principal (SystemPrincipal) if not available. + nsContentPolicyType contentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE; + nsCOMPtr<nsIPrincipal> loadingPrincipal = mContent->NodePrincipal(); + nsAutoString imageLoadingPrincipal; + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::loadingprincipal, + imageLoadingPrincipal); + if (!imageLoadingPrincipal.IsEmpty()) { + nsCOMPtr<nsISupports> serializedPrincipal; + NS_DeserializeObject(NS_ConvertUTF16toUTF8(imageLoadingPrincipal), + getter_AddRefs(serializedPrincipal)); + loadingPrincipal = do_QueryInterface(serializedPrincipal); + + if (loadingPrincipal) { + // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for + // indicating it's a favicon loading. + contentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON; + } else { + // Fallback if the deserialization is failed. + loadingPrincipal = mContent->NodePrincipal(); + } + } + + nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI(); + nsCOMPtr<nsIURI> uri; + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), + src, + doc, + baseURI); + if (uri) { + nsresult rv = nsContentUtils::LoadImage(uri, mContent, doc, loadingPrincipal, + doc->GetDocumentURI(), doc->GetReferrerPolicy(), + mListener, mLoadFlags, + EmptyString(), getter_AddRefs(mImageRequest), + contentPolicyType); + + if (NS_SUCCEEDED(rv) && mImageRequest) { + nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, + mImageRequest, + &mRequestRegistered); + } + } + } + } else { + // Only get the list-style-image if we aren't being drawn + // by a native theme. + uint8_t appearance = StyleDisplay()->mAppearance; + if (!(appearance && nsBox::gTheme && + nsBox::gTheme->ThemeSupportsWidget(nullptr, this, appearance))) { + // get the list-style-image + imgRequestProxy *styleRequest = StyleList()->GetListStyleImage(); + if (styleRequest) { + styleRequest->Clone(mListener, getter_AddRefs(mImageRequest)); + } + } + } + + if (!mImageRequest) { + // We have no image, so size to 0 + mIntrinsicSize.SizeTo(0, 0); + } else { + // We don't want discarding or decode-on-draw for xul images. + mImageRequest->StartDecoding(); + mImageRequest->LockImage(); + } + + // Do this _after_ locking the new image in case they are the same image. + if (oldImageRequest) { + oldImageRequest->UnlockImage(); + } +} + +void +nsImageBoxFrame::UpdateLoadFlags() +{ + static nsIContent::AttrValuesArray strings[] = + {&nsGkAtoms::always, &nsGkAtoms::never, nullptr}; + switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::validate, + strings, eCaseMatters)) { + case 0: + mLoadFlags = nsIRequest::VALIDATE_ALWAYS; + break; + case 1: + mLoadFlags = nsIRequest::VALIDATE_NEVER|nsIRequest::LOAD_FROM_CACHE; + break; + default: + mLoadFlags = nsIRequest::LOAD_NORMAL; + break; + } +} + +void +nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); + + if ((0 == mRect.width) || (0 == mRect.height)) { + // Do not render when given a zero area. This avoids some useless + // scaling work while we wait for our image dimensions to arrive + // asynchronously. + return; + } + + if (!IsVisibleForPainting(aBuilder)) + return; + + uint32_t clipFlags = + nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ? + 0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT; + + DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox + clip(aBuilder, this, clipFlags); + + nsDisplayList list; + list.AppendNewToTop( + new (aBuilder) nsDisplayXULImage(aBuilder, this)); + + CreateOwnLayerIfNeeded(aBuilder, &list); + + aLists.Content()->AppendToTop(&list); +} + +DrawResult +nsImageBoxFrame::PaintImage(nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, nsPoint aPt, + uint32_t aFlags) +{ + nsRect constraintRect; + GetXULClientRect(constraintRect); + + constraintRect += aPt; + + if (!mImageRequest) { + // This probably means we're drawn by a native theme. + return DrawResult::SUCCESS; + } + + // don't draw if the image is not dirty + // XXX(seth): Can this actually happen anymore? + nsRect dirty; + if (!dirty.IntersectRect(aDirtyRect, constraintRect)) { + return DrawResult::TEMPORARY_ERROR; + } + + // Don't draw if the image's size isn't available. + uint32_t imgStatus; + if (!NS_SUCCEEDED(mImageRequest->GetImageStatus(&imgStatus)) || + !(imgStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) { + return DrawResult::NOT_READY; + } + + nsCOMPtr<imgIContainer> imgCon; + mImageRequest->GetImage(getter_AddRefs(imgCon)); + + if (!imgCon) { + return DrawResult::NOT_READY; + } + + bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0); + + Maybe<nsPoint> anchorPoint; + nsRect dest; + if (!mUseSrcAttr) { + // Our image (if we have one) is coming from the CSS property + // 'list-style-image' (combined with '-moz-image-region'). For now, ignore + // 'object-fit' & 'object-position' in this case, and just fill our rect. + // XXXdholbert Should we even honor these properties in this case? They only + // apply to replaced elements, and I'm not sure we count as a replaced + // element when our image data is determined by CSS. + dest = constraintRect; + } else { + // Determine dest rect based on intrinsic size & ratio, along with + // 'object-fit' & 'object-position' properties: + IntrinsicSize intrinsicSize; + nsSize intrinsicRatio; + if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) { + // Image has a valid size; use it as intrinsic size & ratio. + intrinsicSize.width.SetCoordValue(mIntrinsicSize.width); + intrinsicSize.height.SetCoordValue(mIntrinsicSize.height); + intrinsicRatio = mIntrinsicSize; + } else { + // Image doesn't have a (valid) intrinsic size. + // Try to look up intrinsic ratio and use that at least. + imgCon->GetIntrinsicRatio(&intrinsicRatio); + } + anchorPoint.emplace(); + dest = nsLayoutUtils::ComputeObjectDestRect(constraintRect, + intrinsicSize, + intrinsicRatio, + StylePosition(), + anchorPoint.ptr()); + } + + + return nsLayoutUtils::DrawSingleImage( + *aRenderingContext.ThebesContext(), + PresContext(), imgCon, + nsLayoutUtils::GetSamplingFilterForFrame(this), + dest, dirty, nullptr, aFlags, + anchorPoint.ptrOr(nullptr), + hasSubRect ? &mSubRect : nullptr); +} + +void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) +{ + uint32_t flags = imgIContainer::FLAG_NONE; + if (aBuilder->ShouldSyncDecodeImages()) + flags |= imgIContainer::FLAG_SYNC_DECODE; + if (aBuilder->IsPaintingToWindow()) + flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING; + + DrawResult result = static_cast<nsImageBoxFrame*>(mFrame)-> + PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(), flags); + + nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result); +} + +nsDisplayItemGeometry* +nsDisplayXULImage::AllocateGeometry(nsDisplayListBuilder* aBuilder) +{ + return new nsDisplayItemGenericImageGeometry(this, aBuilder); +} + +void +nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, + const nsDisplayItemGeometry* aGeometry, + nsRegion* aInvalidRegion) +{ + auto boxFrame = static_cast<nsImageBoxFrame*>(mFrame); + auto geometry = + static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry); + + if (aBuilder->ShouldSyncDecodeImages() && + boxFrame->mImageRequest && + geometry->ShouldInvalidateToSyncDecodeImages()) { + bool snap; + aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); + } + + nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); +} + +bool +nsDisplayXULImage::CanOptimizeToImageLayer(LayerManager* aManager, + nsDisplayListBuilder* aBuilder) +{ + nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame); + if (!imageFrame->CanOptimizeToImageLayer()) { + return false; + } + + return nsDisplayImageContainer::CanOptimizeToImageLayer(aManager, aBuilder); +} + +already_AddRefed<imgIContainer> +nsDisplayXULImage::GetImage() +{ + nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame); + if (!imageFrame->mImageRequest) { + return nullptr; + } + + nsCOMPtr<imgIContainer> imgCon; + imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon)); + + return imgCon.forget(); +} + +nsRect +nsDisplayXULImage::GetDestRect() +{ + nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame); + + nsRect clientRect; + imageFrame->GetXULClientRect(clientRect); + + return clientRect + ToReferenceFrame(); +} + +bool +nsImageBoxFrame::CanOptimizeToImageLayer() +{ + bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0); + if (hasSubRect) { + return false; + } + return true; +} + +// +// DidSetStyleContext +// +// When the style context changes, make sure that all of our image is up to date. +// +/* virtual */ void +nsImageBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext); + + // Fetch our subrect. + const nsStyleList* myList = StyleList(); + mSubRect = myList->mImageRegion; // before |mSuppressStyleCheck| test! + + if (mUseSrcAttr || mSuppressStyleCheck) + return; // No more work required, since the image isn't specified by style. + + // If we're using a native theme implementation, we shouldn't draw anything. + const nsStyleDisplay* disp = StyleDisplay(); + if (disp->mAppearance && nsBox::gTheme && + nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance)) + return; + + // If list-style-image changes, we have a new image. + nsCOMPtr<nsIURI> oldURI, newURI; + if (mImageRequest) + mImageRequest->GetURI(getter_AddRefs(oldURI)); + if (myList->GetListStyleImage()) + myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI)); + bool equal; + if (newURI == oldURI || // handles null==null + (newURI && oldURI && + NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal)) + return; + + UpdateImage(); +} // DidSetStyleContext + +void +nsImageBoxFrame::GetImageSize() +{ + if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) { + mImageSize.width = mIntrinsicSize.width; + mImageSize.height = mIntrinsicSize.height; + } else { + mImageSize.width = 0; + mImageSize.height = 0; + } +} + +/** + * Ok return our dimensions + */ +nsSize +nsImageBoxFrame::GetXULPrefSize(nsBoxLayoutState& aState) +{ + nsSize size(0,0); + DISPLAY_PREF_SIZE(this, size); + if (DoesNeedRecalc(mImageSize)) + GetImageSize(); + + if (!mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0)) + size = mSubRect.Size(); + else + size = mImageSize; + + nsSize intrinsicSize = size; + + nsMargin borderPadding(0,0,0,0); + GetXULBorderAndPadding(borderPadding); + size.width += borderPadding.LeftRight(); + size.height += borderPadding.TopBottom(); + + bool widthSet, heightSet; + nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet); + NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE, + "non-intrinsic size expected"); + + nsSize minSize = GetXULMinSize(aState); + nsSize maxSize = GetXULMaxSize(aState); + + if (!widthSet && !heightSet) { + if (minSize.width != NS_INTRINSICSIZE) + minSize.width -= borderPadding.LeftRight(); + if (minSize.height != NS_INTRINSICSIZE) + minSize.height -= borderPadding.TopBottom(); + if (maxSize.width != NS_INTRINSICSIZE) + maxSize.width -= borderPadding.LeftRight(); + if (maxSize.height != NS_INTRINSICSIZE) + maxSize.height -= borderPadding.TopBottom(); + + size = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(minSize.width, minSize.height, + maxSize.width, maxSize.height, + intrinsicSize.width, intrinsicSize.height); + NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE, + "non-intrinsic size expected"); + size.width += borderPadding.LeftRight(); + size.height += borderPadding.TopBottom(); + return size; + } + + if (!widthSet) { + if (intrinsicSize.height > 0) { + // Subtract off the border and padding from the height because the + // content-box needs to be used to determine the ratio + nscoord height = size.height - borderPadding.TopBottom(); + size.width = nscoord(int64_t(height) * int64_t(intrinsicSize.width) / + int64_t(intrinsicSize.height)); + } + else { + size.width = intrinsicSize.width; + } + + size.width += borderPadding.LeftRight(); + } + else if (!heightSet) { + if (intrinsicSize.width > 0) { + nscoord width = size.width - borderPadding.LeftRight(); + size.height = nscoord(int64_t(width) * int64_t(intrinsicSize.height) / + int64_t(intrinsicSize.width)); + } + else { + size.height = intrinsicSize.height; + } + + size.height += borderPadding.TopBottom(); + } + + return BoundsCheck(minSize, size, maxSize); +} + +nsSize +nsImageBoxFrame::GetXULMinSize(nsBoxLayoutState& aState) +{ + // An image can always scale down to (0,0). + nsSize size(0,0); + DISPLAY_MIN_SIZE(this, size); + AddBorderAndPadding(size); + bool widthSet, heightSet; + nsIFrame::AddXULMinSize(aState, this, size, widthSet, heightSet); + return size; +} + +nscoord +nsImageBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aState) +{ + return GetXULPrefSize(aState).height; +} + +nsIAtom* +nsImageBoxFrame::GetType() const +{ + return nsGkAtoms::imageBoxFrame; +} + +#ifdef DEBUG_FRAME_DUMP +nsresult +nsImageBoxFrame::GetFrameName(nsAString& aResult) const +{ + return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult); +} +#endif + +nsresult +nsImageBoxFrame::Notify(imgIRequest* aRequest, + int32_t aType, + const nsIntRect* aData) +{ + if (aType == imgINotificationObserver::SIZE_AVAILABLE) { + nsCOMPtr<imgIContainer> image; + aRequest->GetImage(getter_AddRefs(image)); + return OnSizeAvailable(aRequest, image); + } + + if (aType == imgINotificationObserver::DECODE_COMPLETE) { + return OnDecodeComplete(aRequest); + } + + if (aType == imgINotificationObserver::LOAD_COMPLETE) { + uint32_t imgStatus; + aRequest->GetImageStatus(&imgStatus); + nsresult status = + imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; + return OnLoadComplete(aRequest, status); + } + + if (aType == imgINotificationObserver::IS_ANIMATED) { + return OnImageIsAnimated(aRequest); + } + + if (aType == imgINotificationObserver::FRAME_UPDATE) { + return OnFrameUpdate(aRequest); + } + + return NS_OK; +} + +nsresult +nsImageBoxFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) +{ + NS_ENSURE_ARG_POINTER(aImage); + + // Ensure the animation (if any) is started. Note: There is no + // corresponding call to Decrement for this. This Increment will be + // 'cleaned up' by the Request when it is destroyed, but only then. + aRequest->IncrementAnimationConsumers(); + + nscoord w, h; + aImage->GetWidth(&w); + aImage->GetHeight(&h); + + mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w), + nsPresContext::CSSPixelsToAppUnits(h)); + + if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + PresContext()->PresShell()-> + FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); + } + + return NS_OK; +} + +nsresult +nsImageBoxFrame::OnDecodeComplete(imgIRequest* aRequest) +{ + nsBoxLayoutState state(PresContext()); + this->XULRedraw(state); + return NS_OK; +} + +nsresult +nsImageBoxFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus) +{ + if (NS_SUCCEEDED(aStatus)) { + // Fire an onload DOM event. + FireImageDOMEvent(mContent, eLoad); + } else { + // Fire an onerror DOM event. + mIntrinsicSize.SizeTo(0, 0); + PresContext()->PresShell()-> + FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); + FireImageDOMEvent(mContent, eLoadError); + } + + return NS_OK; +} + +nsresult +nsImageBoxFrame::OnImageIsAnimated(imgIRequest* aRequest) +{ + // Register with our refresh driver, if we're animated. + nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest, + &mRequestRegistered); + + return NS_OK; +} + +nsresult +nsImageBoxFrame::OnFrameUpdate(imgIRequest* aRequest) +{ + if ((0 == mRect.width) || (0 == mRect.height)) { + return NS_OK; + } + + InvalidateLayer(nsDisplayItem::TYPE_XUL_IMAGE); + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver, imgIOnloadBlocker) + +nsImageBoxListener::nsImageBoxListener() +{ +} + +nsImageBoxListener::~nsImageBoxListener() +{ +} + +NS_IMETHODIMP +nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData) +{ + if (!mFrame) + return NS_OK; + + return mFrame->Notify(request, aType, aData); +} + +NS_IMETHODIMP +nsImageBoxListener::BlockOnload(imgIRequest *aRequest) +{ + if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetUncomposedDoc()) { + mFrame->GetContent()->GetUncomposedDoc()->BlockOnload(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsImageBoxListener::UnblockOnload(imgIRequest *aRequest) +{ + if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetUncomposedDoc()) { + mFrame->GetContent()->GetUncomposedDoc()->UnblockOnload(false); + } + + return NS_OK; +} |