gecko-dev/layout/xul/nsImageBoxFrame.cpp
Nathan Froyd 0bc5179e71 Bug 1153253 - move nsImageBoxFrame::mRequestRegistered to pack better with other members; r=dholbert
Given alignment requirements on most platforms, there will be at least 3
bytes of padding after mRequestRegistered in its current location, and
at least one unused byte at the end of the structure.  We can move
mRequestRegistered after mLoadFlags so that it packs properly with the
boolean flags already there for less wasted space.
2015-04-09 12:56:25 -04:00

807 lines
23 KiB
C++

/* -*- 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 "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 "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "nsDisplayList.h"
#include "ImageLayers.h"
#include "ImageContainer.h"
#include "nsIContent.h"
#include "nsContentUtils.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/EventDispatcher.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 nsRunnable
{
public:
nsImageBoxFrameEvent(nsIContent *content, uint32_t message)
: mContent(content), mMessage(message) {}
NS_IMETHOD Run() override;
private:
nsCOMPtr<nsIContent> mContent;
uint32_t mMessage;
};
NS_IMETHODIMP
nsImageBoxFrameEvent::Run()
{
nsIPresShell *pres_shell = mContent->OwnerDoc()->GetShell();
if (!pres_shell) {
return NS_OK;
}
nsRefPtr<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, uint32_t aMessage)
{
NS_ASSERTION(aMessage == NS_LOAD || aMessage == NS_LOAD_ERROR,
"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),
mFireEventOnDecode(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) {
nsRefPtr<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();
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) {
// No need to do anything here...
return;
}
nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
nsCOMPtr<nsIURI> uri;
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
src,
doc,
baseURI);
if (uri && nsContentUtils::CanLoadImage(uri, mContent, doc,
mContent->NodePrincipal())) {
nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(),
doc->GetDocumentURI(), doc->GetReferrerPolicy(),
mListener, mLoadFlags,
EmptyString(), getter_AddRefs(mImageRequest));
if (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();
}
}
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;
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 rect;
GetClientRect(rect);
rect += 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, rect)) {
return DrawResult::TEMPORARY_ERROR;
}
nsCOMPtr<imgIContainer> imgCon;
mImageRequest->GetImage(getter_AddRefs(imgCon));
if (imgCon) {
bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
return
nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
PresContext(), imgCon,
nsLayoutUtils::GetGraphicsFilterForFrame(this),
rect, dirty, nullptr, aFlags, nullptr,
hasSubRect ? &mSubRect : nullptr);
}
return DrawResult::NOT_READY;
}
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);
}
void
nsDisplayXULImage::ConfigureLayer(ImageLayer* aLayer,
const ContainerLayerParameters& aParameters)
{
aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
nsRect clientRect;
imageFrame->GetClientRect(clientRect);
const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
const LayoutDeviceRect destRect =
LayoutDeviceRect::FromAppUnits(clientRect + ToReferenceFrame(), factor);
nsCOMPtr<imgIContainer> imgCon;
imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon));
int32_t imageWidth;
int32_t imageHeight;
imgCon->GetWidth(&imageWidth);
imgCon->GetHeight(&imageHeight);
NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
if (imageWidth > 0 && imageHeight > 0) {
// We're actually using the ImageContainer. Let our frame know that it
// should consider itself to have painted successfully.
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this,
DrawResult::SUCCESS);
}
// XXX(seth): Right now we ignore aParameters.Scale() and
// aParameters.Offset(), because FrameLayerBuilder already applies
// aParameters.Scale() via the layer's post-transform, and
// aParameters.Offset() is always zero.
MOZ_ASSERT(aParameters.Offset() == LayerIntPoint(0,0));
const LayoutDevicePoint p = destRect.TopLeft();
Matrix transform = Matrix::Translation(p.x, p.y);
transform.PreScale(destRect.Width() / imageWidth,
destRect.Height() / imageHeight);
aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
}
already_AddRefed<ImageContainer>
nsDisplayXULImage::GetContainer(LayerManager* aManager,
nsDisplayListBuilder* aBuilder)
{
uint32_t flags = aBuilder->ShouldSyncDecodeImages()
? imgIContainer::FLAG_SYNC_DECODE
: imgIContainer::FLAG_NONE;
return static_cast<nsImageBoxFrame*>(mFrame)->GetContainer(aManager, flags);
}
already_AddRefed<ImageContainer>
nsImageBoxFrame::GetContainer(LayerManager* aManager, uint32_t aFlags)
{
bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
if (hasSubRect || !mImageRequest) {
return nullptr;
}
nsCOMPtr<imgIContainer> imgCon;
mImageRequest->GetImage(getter_AddRefs(imgCon));
if (!imgCon) {
return nullptr;
}
return imgCon->GetImageContainer(aManager, aFlags);
}
//
// 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::GetPrefSize(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);
GetBorderAndPadding(borderPadding);
size.width += borderPadding.LeftRight();
size.height += borderPadding.TopBottom();
bool widthSet, heightSet;
nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet);
NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
"non-intrinsic size expected");
nsSize minSize = GetMinSize(aState);
nsSize maxSize = GetMaxSize(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::GetMinSize(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::AddCSSMinSize(aState, this, size, widthSet, heightSet);
return size;
}
nscoord
nsImageBoxFrame::GetBoxAscent(nsBoxLayoutState& aState)
{
return GetPrefSize(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)
{
if (mFireEventOnDecode) {
mFireEventOnDecode = false;
uint32_t reqStatus;
aRequest->GetImageStatus(&reqStatus);
if (!(reqStatus & imgIRequest::STATUS_ERROR)) {
FireImageDOMEvent(mContent, NS_LOAD);
} else {
// Fire an onerror DOM event.
mIntrinsicSize.SizeTo(0, 0);
PresContext()->PresShell()->
FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
FireImageDOMEvent(mContent, NS_LOAD_ERROR);
}
}
nsBoxLayoutState state(PresContext());
this->Redraw(state);
return NS_OK;
}
nsresult
nsImageBoxFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
{
uint32_t reqStatus;
aRequest->GetImageStatus(&reqStatus);
// We want to give the decoder a chance to find errors. If we haven't found
// an error yet and we've already started decoding, we must only fire these
// events after we finish decoding.
if (NS_SUCCEEDED(aStatus) && !(reqStatus & imgIRequest::STATUS_ERROR) &&
(reqStatus & imgIRequest::STATUS_DECODE_STARTED) &&
!(reqStatus & imgIRequest::STATUS_DECODE_COMPLETE)) {
mFireEventOnDecode = true;
} else {
if (NS_SUCCEEDED(aStatus)) {
// Fire an onload DOM event.
FireImageDOMEvent(mContent, NS_LOAD);
} else {
// Fire an onerror DOM event.
mIntrinsicSize.SizeTo(0, 0);
PresContext()->PresShell()->
FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
FireImageDOMEvent(mContent, NS_LOAD_ERROR);
}
}
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);
}
/* void blockOnload (in imgIRequest aRequest); */
NS_IMETHODIMP
nsImageBoxListener::BlockOnload(imgIRequest *aRequest)
{
if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) {
mFrame->GetContent()->GetCurrentDoc()->BlockOnload();
}
return NS_OK;
}
/* void unblockOnload (in imgIRequest aRequest); */
NS_IMETHODIMP
nsImageBoxListener::UnblockOnload(imgIRequest *aRequest)
{
if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetCurrentDoc()) {
mFrame->GetContent()->GetCurrentDoc()->UnblockOnload(false);
}
return NS_OK;
}