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: // Keep in (case-insensitive) order: michael@0: #include "gfxContext.h" michael@0: #include "gfxPlatform.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "imgIContainer.h" michael@0: #include "nsIImageLoadingContent.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "imgINotificationObserver.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "nsSVGPathGeometryFrame.h" michael@0: #include "mozilla/dom/SVGSVGElement.h" michael@0: #include "nsSVGUtils.h" michael@0: #include "SVGContentUtils.h" michael@0: #include "SVGImageContext.h" michael@0: #include "mozilla/dom/SVGImageElement.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIReflowCallback.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::gfx; michael@0: michael@0: class nsSVGImageFrame; michael@0: michael@0: class nsSVGImageListener MOZ_FINAL : public imgINotificationObserver michael@0: { michael@0: public: michael@0: nsSVGImageListener(nsSVGImageFrame *aFrame); michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_IMGINOTIFICATIONOBSERVER michael@0: michael@0: void SetFrame(nsSVGImageFrame *frame) { mFrame = frame; } michael@0: michael@0: private: michael@0: nsSVGImageFrame *mFrame; michael@0: }; michael@0: michael@0: typedef nsSVGPathGeometryFrame nsSVGImageFrameBase; michael@0: michael@0: class nsSVGImageFrame : public nsSVGImageFrameBase, michael@0: public nsIReflowCallback michael@0: { michael@0: friend nsIFrame* michael@0: NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: protected: michael@0: nsSVGImageFrame(nsStyleContext* aContext) : nsSVGImageFrameBase(aContext), michael@0: mReflowCallbackPosted(false) {} michael@0: virtual ~nsSVGImageFrame(); michael@0: michael@0: public: michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: // nsISVGChildFrame interface: michael@0: virtual nsresult PaintSVG(nsRenderingContext *aContext, michael@0: const nsIntRect *aDirtyRect, michael@0: nsIFrame* aTransformRoot) MOZ_OVERRIDE; michael@0: virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE; michael@0: virtual void ReflowSVG() MOZ_OVERRIDE; michael@0: michael@0: // nsSVGPathGeometryFrame methods: michael@0: virtual uint16_t GetHitTestFlags() MOZ_OVERRIDE; michael@0: michael@0: // nsIFrame interface: michael@0: virtual nsresult AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) MOZ_OVERRIDE; michael@0: virtual void Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) MOZ_OVERRIDE; michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Get the "type" of the frame michael@0: * michael@0: * @see nsGkAtoms::svgImageFrame michael@0: */ michael@0: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("SVGImage"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: // nsIReflowCallback michael@0: virtual bool ReflowFinished() MOZ_OVERRIDE; michael@0: virtual void ReflowCallbackCanceled() MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: gfx::Matrix GetRasterImageTransform(int32_t aNativeWidth, michael@0: int32_t aNativeHeight, michael@0: uint32_t aFor, michael@0: nsIFrame* aTransformRoot = nullptr); michael@0: gfx::Matrix GetVectorImageTransform(uint32_t aFor, michael@0: nsIFrame* aTransformRoot = nullptr); michael@0: bool TransformContextForPainting(gfxContext* aGfxContext, michael@0: nsIFrame* aTransformRoot); michael@0: michael@0: nsCOMPtr mListener; michael@0: michael@0: nsCOMPtr mImageContainer; michael@0: michael@0: bool mReflowCallbackPosted; michael@0: michael@0: friend class nsSVGImageListener; michael@0: }; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Implementation michael@0: michael@0: nsIFrame* michael@0: NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsSVGImageFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGImageFrame) michael@0: michael@0: nsSVGImageFrame::~nsSVGImageFrame() michael@0: { michael@0: // set the frame to null so we don't send messages to a dead object. michael@0: if (mListener) { michael@0: nsCOMPtr imageLoader = do_QueryInterface(mContent); michael@0: if (imageLoader) { michael@0: imageLoader->RemoveObserver(mListener); michael@0: } michael@0: reinterpret_cast(mListener.get())->SetFrame(nullptr); michael@0: } michael@0: mListener = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsSVGImageFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_ASSERTION(aContent->IsSVG(nsGkAtoms::image), michael@0: "Content is not an SVG image!"); michael@0: michael@0: nsSVGImageFrameBase::Init(aContent, aParent, aPrevInFlow); michael@0: michael@0: mListener = new nsSVGImageListener(this); michael@0: nsCOMPtr imageLoader = do_QueryInterface(mContent); michael@0: if (!imageLoader) { michael@0: NS_RUNTIMEABORT("Why is this not an image loading content?"); michael@0: } michael@0: michael@0: // We should have a PresContext now, so let's notify our image loader that michael@0: // we need to register any image animations with the refresh driver. michael@0: imageLoader->FrameCreated(this); michael@0: michael@0: imageLoader->AddObserver(mListener); michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsSVGImageFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: if (mReflowCallbackPosted) { michael@0: PresContext()->PresShell()->CancelReflowCallback(this); michael@0: mReflowCallbackPosted = false; michael@0: } michael@0: michael@0: nsCOMPtr imageLoader = michael@0: do_QueryInterface(nsFrame::mContent); michael@0: michael@0: if (imageLoader) { michael@0: imageLoader->FrameDestroyed(this); michael@0: } michael@0: michael@0: nsFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsIFrame methods: michael@0: michael@0: nsresult michael@0: nsSVGImageFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aNameSpaceID == kNameSpaceID_None) { michael@0: if (aAttribute == nsGkAtoms::x || michael@0: aAttribute == nsGkAtoms::y || michael@0: aAttribute == nsGkAtoms::width || michael@0: aAttribute == nsGkAtoms::height) { michael@0: nsSVGEffects::InvalidateRenderingObservers(this); michael@0: nsSVGUtils::ScheduleReflowSVG(this); michael@0: return NS_OK; michael@0: } michael@0: else if (aAttribute == nsGkAtoms::preserveAspectRatio) { michael@0: // We don't paint the content of the image using display lists, therefore michael@0: // we have to invalidate for this children-only transform changes since michael@0: // there is no layer tree to notice that the transform changed and michael@0: // recomposite. michael@0: InvalidateFrame(); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: if (aNameSpaceID == kNameSpaceID_XLink && michael@0: aAttribute == nsGkAtoms::href) { michael@0: michael@0: // Prevent setting image.src by exiting early michael@0: if (nsContentUtils::IsImageSrcSetDisabled()) { michael@0: return NS_OK; michael@0: } michael@0: SVGImageElement *element = static_cast(mContent); michael@0: michael@0: if (element->mStringAttributes[SVGImageElement::HREF].IsExplicitlySet()) { michael@0: element->LoadSVGImage(true, true); michael@0: } else { michael@0: element->CancelImageRequests(true); michael@0: } michael@0: } michael@0: michael@0: return nsSVGImageFrameBase::AttributeChanged(aNameSpaceID, michael@0: aAttribute, aModType); michael@0: } michael@0: michael@0: gfx::Matrix michael@0: nsSVGImageFrame::GetRasterImageTransform(int32_t aNativeWidth, michael@0: int32_t aNativeHeight, michael@0: uint32_t aFor, michael@0: nsIFrame* aTransformRoot) michael@0: { michael@0: float x, y, width, height; michael@0: SVGImageElement *element = static_cast(mContent); michael@0: element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); michael@0: michael@0: Matrix viewBoxTM = michael@0: SVGContentUtils::GetViewBoxTransform(width, height, michael@0: 0, 0, aNativeWidth, aNativeHeight, michael@0: element->mPreserveAspectRatio); michael@0: michael@0: return viewBoxTM * michael@0: gfx::Matrix::Translation(x, y) * michael@0: gfx::ToMatrix(GetCanvasTM(aFor, aTransformRoot)); michael@0: } michael@0: michael@0: gfx::Matrix michael@0: nsSVGImageFrame::GetVectorImageTransform(uint32_t aFor, michael@0: nsIFrame* aTransformRoot) michael@0: { michael@0: float x, y, width, height; michael@0: SVGImageElement *element = static_cast(mContent); michael@0: element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); michael@0: michael@0: // No viewBoxTM needed here -- our height/width overrides any concept of michael@0: // "native size" that the SVG image has, and it will handle viewBox and michael@0: // preserveAspectRatio on its own once we give it a region to draw into. michael@0: michael@0: return gfx::Matrix::Translation(x, y) * michael@0: gfx::ToMatrix(GetCanvasTM(aFor, aTransformRoot)); michael@0: } michael@0: michael@0: bool michael@0: nsSVGImageFrame::TransformContextForPainting(gfxContext* aGfxContext, michael@0: nsIFrame* aTransformRoot) michael@0: { michael@0: gfx::Matrix imageTransform; michael@0: if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) { michael@0: imageTransform = GetVectorImageTransform(FOR_PAINTING, aTransformRoot); michael@0: } else { michael@0: int32_t nativeWidth, nativeHeight; michael@0: if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) || michael@0: NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) || michael@0: nativeWidth == 0 || nativeHeight == 0) { michael@0: return false; michael@0: } michael@0: imageTransform = michael@0: GetRasterImageTransform(nativeWidth, nativeHeight, FOR_PAINTING, michael@0: aTransformRoot); michael@0: michael@0: // NOTE: We need to cancel out the effects of Full-Page-Zoom, or else michael@0: // it'll get applied an extra time by DrawSingleUnscaledImage. michael@0: nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); michael@0: gfxFloat pageZoomFactor = michael@0: nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPx); michael@0: imageTransform.Scale(pageZoomFactor, pageZoomFactor); michael@0: } michael@0: michael@0: if (imageTransform.IsSingular()) { michael@0: return false; michael@0: } michael@0: michael@0: aGfxContext->Multiply(ThebesMatrix(imageTransform)); michael@0: return true; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsISVGChildFrame methods: michael@0: nsresult michael@0: nsSVGImageFrame::PaintSVG(nsRenderingContext *aContext, michael@0: const nsIntRect *aDirtyRect, michael@0: nsIFrame* aTransformRoot) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!StyleVisibility()->IsVisible()) michael@0: return NS_OK; michael@0: michael@0: float x, y, width, height; michael@0: SVGImageElement *imgElem = static_cast(mContent); michael@0: imgElem->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); michael@0: NS_ASSERTION(width > 0 && height > 0, michael@0: "Should only be painting things with valid width/height"); michael@0: michael@0: if (!mImageContainer) { michael@0: nsCOMPtr currentRequest; michael@0: nsCOMPtr imageLoader = do_QueryInterface(mContent); michael@0: if (imageLoader) michael@0: imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, michael@0: getter_AddRefs(currentRequest)); michael@0: michael@0: if (currentRequest) michael@0: currentRequest->GetImage(getter_AddRefs(mImageContainer)); michael@0: } michael@0: michael@0: if (mImageContainer) { michael@0: gfxContext* ctx = aContext->ThebesContext(); michael@0: gfxContextAutoSaveRestore autoRestorer(ctx); michael@0: michael@0: if (StyleDisplay()->IsScrollableOverflow()) { michael@0: gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, x, y, michael@0: width, height); michael@0: nsSVGUtils::SetClipRect(ctx, GetCanvasTM(FOR_PAINTING, aTransformRoot), michael@0: clipRect); michael@0: } michael@0: michael@0: if (!TransformContextForPainting(ctx, aTransformRoot)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // fill-opacity doesn't affect , so if we're allowed to michael@0: // optimize group opacity, the opacity used for compositing the michael@0: // image into the current canvas is just the group opacity. michael@0: float opacity = 1.0f; michael@0: if (nsSVGUtils::CanOptimizeOpacity(this)) { michael@0: opacity = StyleDisplay()->mOpacity; michael@0: } michael@0: michael@0: if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { michael@0: ctx->PushGroup(gfxContentType::COLOR_ALPHA); michael@0: } michael@0: michael@0: nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); michael@0: nsRect dirtyRect; // only used if aDirtyRect is non-null michael@0: if (aDirtyRect) { michael@0: NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || michael@0: (mState & NS_FRAME_IS_NONDISPLAY), michael@0: "Display lists handle dirty rect intersection test"); michael@0: dirtyRect = aDirtyRect->ToAppUnits(appUnitsPerDevPx); michael@0: // Adjust dirtyRect to match our local coordinate system. michael@0: nsRect rootRect = michael@0: nsSVGUtils::TransformFrameRectToOuterSVG(mRect, michael@0: GetCanvasTM(FOR_PAINTING), PresContext()); michael@0: dirtyRect.MoveBy(-rootRect.TopLeft()); michael@0: } michael@0: michael@0: // XXXbholley - I don't think huge images in SVGs are common enough to michael@0: // warrant worrying about the responsiveness impact of doing synchronous michael@0: // decodes. The extra code complexity of determinining when we want to michael@0: // force sync probably just isn't worth it, so always pass FLAG_SYNC_DECODE michael@0: uint32_t drawFlags = imgIContainer::FLAG_SYNC_DECODE; michael@0: michael@0: if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) { michael@0: // Package up the attributes of this image element which can override the michael@0: // attributes of mImageContainer's internal SVG document. michael@0: SVGImageContext context(imgElem->mPreserveAspectRatio.GetAnimValue()); michael@0: michael@0: nsRect destRect(0, 0, michael@0: appUnitsPerDevPx * width, michael@0: appUnitsPerDevPx * height); michael@0: michael@0: // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case. michael@0: // That method needs our image to have a fixed native width & height, michael@0: // and that's not always true for TYPE_VECTOR images. michael@0: nsLayoutUtils::DrawSingleImage( michael@0: aContext, michael@0: mImageContainer, michael@0: nsLayoutUtils::GetGraphicsFilterForFrame(this), michael@0: destRect, michael@0: aDirtyRect ? dirtyRect : destRect, michael@0: &context, michael@0: drawFlags); michael@0: } else { // mImageContainer->GetType() == TYPE_RASTER michael@0: nsLayoutUtils::DrawSingleUnscaledImage( michael@0: aContext, michael@0: mImageContainer, michael@0: nsLayoutUtils::GetGraphicsFilterForFrame(this), michael@0: nsPoint(0, 0), michael@0: aDirtyRect ? &dirtyRect : nullptr, michael@0: drawFlags); michael@0: } michael@0: michael@0: if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { michael@0: ctx->PopGroupToSource(); michael@0: ctx->SetOperator(gfxContext::OPERATOR_OVER); michael@0: ctx->Paint(opacity); michael@0: } michael@0: // gfxContextAutoSaveRestore goes out of scope & cleans up our gfxContext michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsSVGImageFrame::GetFrameForPoint(const nsPoint &aPoint) michael@0: { michael@0: // Special case for raster images -- we only want to accept points that fall michael@0: // in the underlying image's (transformed) native bounds. That region michael@0: // doesn't necessarily map to our element's [x,y,width,height]. So, michael@0: // we have to look up the native image size & our image transform in order michael@0: // to filter out points that fall outside that area. michael@0: if (StyleDisplay()->IsScrollableOverflow() && mImageContainer) { michael@0: if (mImageContainer->GetType() == imgIContainer::TYPE_RASTER) { michael@0: int32_t nativeWidth, nativeHeight; michael@0: if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) || michael@0: NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) || michael@0: nativeWidth == 0 || nativeHeight == 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!nsSVGUtils::HitTestRect( michael@0: GetRasterImageTransform(nativeWidth, nativeHeight, michael@0: FOR_HIT_TESTING), michael@0: 0, 0, nativeWidth, nativeHeight, michael@0: PresContext()->AppUnitsToDevPixels(aPoint.x), michael@0: PresContext()->AppUnitsToDevPixels(aPoint.y))) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: // The special case above doesn't apply to vector images, because they michael@0: // don't limit their drawing to explicit "native bounds" -- they have michael@0: // an infinite canvas on which to place content. So it's reasonable to michael@0: // just fall back on our element's own bounds here. michael@0: } michael@0: michael@0: return nsSVGImageFrameBase::GetFrameForPoint(aPoint); michael@0: } michael@0: michael@0: nsIAtom * michael@0: nsSVGImageFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::svgImageFrame; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsSVGPathGeometryFrame methods: michael@0: michael@0: // Lie about our fill/stroke so that covered region and hit detection work properly michael@0: michael@0: void michael@0: nsSVGImageFrame::ReflowSVG() michael@0: { michael@0: NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), michael@0: "This call is probably a wasteful mistake"); michael@0: michael@0: NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), michael@0: "ReflowSVG mechanism not designed for this"); michael@0: michael@0: if (!nsSVGUtils::NeedsReflowSVG(this)) { michael@0: return; michael@0: } michael@0: michael@0: float x, y, width, height; michael@0: SVGImageElement *element = static_cast(mContent); michael@0: element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); michael@0: michael@0: Rect extent(x, y, width, height); michael@0: michael@0: if (!extent.IsEmpty()) { michael@0: mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, michael@0: PresContext()->AppUnitsPerCSSPixel()); michael@0: } else { michael@0: mRect.SetEmpty(); michael@0: } michael@0: michael@0: if (mState & NS_FRAME_FIRST_REFLOW) { michael@0: // Make sure we have our filter property (if any) before calling michael@0: // FinishAndStoreOverflow (subsequent filter changes are handled off michael@0: // nsChangeHint_UpdateEffects): michael@0: nsSVGEffects::UpdateEffects(this); michael@0: michael@0: if (!mReflowCallbackPosted) { michael@0: nsIPresShell* shell = PresContext()->PresShell(); michael@0: mReflowCallbackPosted = true; michael@0: shell->PostReflowCallback(this); michael@0: } michael@0: } michael@0: michael@0: nsRect overflow = nsRect(nsPoint(0,0), mRect.Size()); michael@0: nsOverflowAreas overflowAreas(overflow, overflow); michael@0: FinishAndStoreOverflow(overflowAreas, mRect.Size()); michael@0: michael@0: mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: michael@0: // Invalidate, but only if this is not our first reflow (since if it is our michael@0: // first reflow then we haven't had our first paint yet). michael@0: if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { michael@0: InvalidateFrame(); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsSVGImageFrame::ReflowFinished() michael@0: { michael@0: mReflowCallbackPosted = false; michael@0: michael@0: nsLayoutUtils::UpdateImageVisibilityForFrame(this); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsSVGImageFrame::ReflowCallbackCanceled() michael@0: { michael@0: mReflowCallbackPosted = false; michael@0: } michael@0: michael@0: uint16_t michael@0: nsSVGImageFrame::GetHitTestFlags() michael@0: { michael@0: uint16_t flags = 0; michael@0: michael@0: switch(StyleVisibility()->mPointerEvents) { michael@0: case NS_STYLE_POINTER_EVENTS_NONE: michael@0: break; michael@0: case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED: michael@0: case NS_STYLE_POINTER_EVENTS_AUTO: michael@0: if (StyleVisibility()->IsVisible()) { michael@0: /* XXX: should check pixel transparency */ michael@0: flags |= SVG_HIT_TEST_FILL; michael@0: } michael@0: break; michael@0: case NS_STYLE_POINTER_EVENTS_VISIBLEFILL: michael@0: case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE: michael@0: case NS_STYLE_POINTER_EVENTS_VISIBLE: michael@0: if (StyleVisibility()->IsVisible()) { michael@0: flags |= SVG_HIT_TEST_FILL; michael@0: } michael@0: break; michael@0: case NS_STYLE_POINTER_EVENTS_PAINTED: michael@0: /* XXX: should check pixel transparency */ michael@0: flags |= SVG_HIT_TEST_FILL; michael@0: break; michael@0: case NS_STYLE_POINTER_EVENTS_FILL: michael@0: case NS_STYLE_POINTER_EVENTS_STROKE: michael@0: case NS_STYLE_POINTER_EVENTS_ALL: michael@0: flags |= SVG_HIT_TEST_FILL; michael@0: break; michael@0: default: michael@0: NS_ERROR("not reached"); michael@0: break; michael@0: } michael@0: michael@0: return flags; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsSVGImageListener implementation michael@0: michael@0: NS_IMPL_ISUPPORTS(nsSVGImageListener, imgINotificationObserver) michael@0: michael@0: nsSVGImageListener::nsSVGImageListener(nsSVGImageFrame *aFrame) : mFrame(aFrame) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSVGImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) michael@0: { michael@0: if (!mFrame) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (aType == imgINotificationObserver::LOAD_COMPLETE) { michael@0: mFrame->InvalidateFrame(); michael@0: nsSVGEffects::InvalidateRenderingObservers(mFrame); michael@0: nsSVGUtils::ScheduleReflowSVG(mFrame); michael@0: } michael@0: michael@0: if (aType == imgINotificationObserver::FRAME_UPDATE) { michael@0: // No new dimensions, so we don't need to call michael@0: // nsSVGUtils::InvalidateAndScheduleBoundsUpdate. michael@0: nsSVGEffects::InvalidateRenderingObservers(mFrame); michael@0: mFrame->InvalidateFrame(); michael@0: } michael@0: michael@0: if (aType == imgINotificationObserver::SIZE_AVAILABLE) { michael@0: // Called once the resource's dimensions have been obtained. michael@0: aRequest->GetImage(getter_AddRefs(mFrame->mImageContainer)); michael@0: mFrame->InvalidateFrame(); michael@0: nsSVGEffects::InvalidateRenderingObservers(mFrame); michael@0: nsSVGUtils::ScheduleReflowSVG(mFrame); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: