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: // Main header first: michael@0: #include "nsSVGMarkerFrame.h" michael@0: michael@0: // Keep others in (case-insensitive) order: michael@0: #include "gfxContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "mozilla/dom/SVGMarkerElement.h" michael@0: #include "nsSVGPathGeometryElement.h" michael@0: #include "nsSVGPathGeometryFrame.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::gfx; michael@0: michael@0: nsIFrame* michael@0: NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsSVGMarkerFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerFrame) michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsIFrame methods: michael@0: michael@0: nsresult michael@0: nsSVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aNameSpaceID == kNameSpaceID_None && michael@0: (aAttribute == nsGkAtoms::markerUnits || michael@0: aAttribute == nsGkAtoms::refX || michael@0: aAttribute == nsGkAtoms::refY || michael@0: aAttribute == nsGkAtoms::markerWidth || michael@0: aAttribute == nsGkAtoms::markerHeight || michael@0: aAttribute == nsGkAtoms::orient || michael@0: aAttribute == nsGkAtoms::preserveAspectRatio || michael@0: aAttribute == nsGkAtoms::viewBox)) { michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } michael@0: michael@0: return nsSVGMarkerFrameBase::AttributeChanged(aNameSpaceID, michael@0: aAttribute, aModType); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsSVGMarkerFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_ASSERTION(aContent->IsSVG(nsGkAtoms::marker), "Content is not an SVG marker"); michael@0: michael@0: nsSVGMarkerFrameBase::Init(aContent, aParent, aPrevInFlow); michael@0: } michael@0: #endif /* DEBUG */ michael@0: michael@0: nsIAtom * michael@0: nsSVGMarkerFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::svgMarkerFrame; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsSVGContainerFrame methods: michael@0: michael@0: gfxMatrix michael@0: nsSVGMarkerFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) michael@0: { michael@0: NS_ASSERTION(mMarkedFrame, "null nsSVGPathGeometry frame"); michael@0: michael@0: if (mInUse2) { michael@0: // We're going to be bailing drawing the marker, so return an identity. michael@0: return gfxMatrix(); michael@0: } michael@0: michael@0: SVGMarkerElement *content = static_cast(mContent); michael@0: michael@0: mInUse2 = true; michael@0: gfxMatrix markedTM = mMarkedFrame->GetCanvasTM(aFor, aTransformRoot); michael@0: mInUse2 = false; michael@0: michael@0: Matrix markerTM = content->GetMarkerTransform(mStrokeWidth, mX, mY, michael@0: mAutoAngle, mIsStart); michael@0: Matrix viewBoxTM = content->GetViewBoxTransform(); michael@0: michael@0: return ThebesMatrix(viewBoxTM * markerTM) * markedTM; michael@0: } michael@0: michael@0: static nsIFrame* michael@0: GetAnonymousChildFrame(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* kid = aFrame->GetFirstPrincipalChild(); michael@0: MOZ_ASSERT(kid && kid->GetType() == nsGkAtoms::svgMarkerAnonChildFrame, michael@0: "expected to find anonymous child of marker frame"); michael@0: return kid; michael@0: } michael@0: michael@0: nsresult michael@0: nsSVGMarkerFrame::PaintMark(nsRenderingContext *aContext, michael@0: nsSVGPathGeometryFrame *aMarkedFrame, michael@0: nsSVGMark *aMark, float aStrokeWidth) michael@0: { michael@0: // If the flag is set when we get here, it means this marker frame michael@0: // has already been used painting the current mark, and the document michael@0: // has a marker reference loop. michael@0: if (mInUse) michael@0: return NS_OK; michael@0: michael@0: AutoMarkerReferencer markerRef(this, aMarkedFrame); michael@0: michael@0: SVGMarkerElement *marker = static_cast(mContent); michael@0: if (!marker->HasValidDimensions()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: const nsSVGViewBoxRect viewBox = marker->GetViewBoxRect(); michael@0: michael@0: if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) { michael@0: // We must disable rendering if the viewBox width or height are zero. michael@0: return NS_OK; michael@0: } michael@0: michael@0: mStrokeWidth = aStrokeWidth; michael@0: mX = aMark->x; michael@0: mY = aMark->y; michael@0: mAutoAngle = aMark->angle; michael@0: mIsStart = aMark->type == nsSVGMark::eStart; michael@0: michael@0: gfxContext *gfx = aContext->ThebesContext(); michael@0: michael@0: if (StyleDisplay()->IsScrollableOverflow()) { michael@0: gfx->Save(); michael@0: gfxRect clipRect = michael@0: nsSVGUtils::GetClipRectForFrame(this, viewBox.x, viewBox.y, michael@0: viewBox.width, viewBox.height); michael@0: nsSVGUtils::SetClipRect(gfx, GetCanvasTM(nsISVGChildFrame::FOR_PAINTING), michael@0: clipRect); michael@0: } michael@0: michael@0: nsIFrame* kid = GetAnonymousChildFrame(this); michael@0: nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); michael@0: // The CTM of each frame referencing us may be different. michael@0: SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); michael@0: nsSVGUtils::PaintFrameWithEffects(aContext, nullptr, kid); michael@0: michael@0: if (StyleDisplay()->IsScrollableOverflow()) michael@0: gfx->Restore(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: SVGBBox michael@0: nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace, michael@0: uint32_t aFlags, michael@0: nsSVGPathGeometryFrame *aMarkedFrame, michael@0: const nsSVGMark *aMark, michael@0: float aStrokeWidth) michael@0: { michael@0: SVGBBox bbox; michael@0: michael@0: // If the flag is set when we get here, it means this marker frame michael@0: // has already been used in calculating the current mark bbox, and michael@0: // the document has a marker reference loop. michael@0: if (mInUse) michael@0: return bbox; michael@0: michael@0: AutoMarkerReferencer markerRef(this, aMarkedFrame); michael@0: michael@0: SVGMarkerElement *content = static_cast(mContent); michael@0: if (!content->HasValidDimensions()) { michael@0: return bbox; michael@0: } michael@0: michael@0: const nsSVGViewBoxRect viewBox = content->GetViewBoxRect(); michael@0: michael@0: if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) { michael@0: return bbox; michael@0: } michael@0: michael@0: mStrokeWidth = aStrokeWidth; michael@0: mX = aMark->x; michael@0: mY = aMark->y; michael@0: mAutoAngle = aMark->angle; michael@0: mIsStart = aMark->type == nsSVGMark::eStart; michael@0: michael@0: Matrix markerTM = michael@0: content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle, mIsStart); michael@0: Matrix viewBoxTM = content->GetViewBoxTransform(); michael@0: michael@0: Matrix tm = viewBoxTM * markerTM * aToBBoxUserspace; michael@0: michael@0: nsISVGChildFrame* child = do_QueryFrame(GetAnonymousChildFrame(this)); michael@0: // When we're being called to obtain the invalidation area, we need to michael@0: // pass down all the flags so that stroke is included. However, once DOM michael@0: // getBBox() accepts flags, maybe we should strip some of those here? michael@0: michael@0: // We need to include zero width/height vertical/horizontal lines, so we have michael@0: // to use UnionEdges. michael@0: bbox.UnionEdges(child->GetBBoxContribution(tm, aFlags)); michael@0: michael@0: return bbox; michael@0: } michael@0: michael@0: void michael@0: nsSVGMarkerFrame::SetParentCoordCtxProvider(SVGSVGElement *aContext) michael@0: { michael@0: SVGMarkerElement *marker = static_cast(mContent); michael@0: marker->SetParentCoordCtxProvider(aContext); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // helper class michael@0: michael@0: nsSVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer( michael@0: nsSVGMarkerFrame *aFrame, michael@0: nsSVGPathGeometryFrame *aMarkedFrame michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) michael@0: : mFrame(aFrame) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: mFrame->mInUse = true; michael@0: mFrame->mMarkedFrame = aMarkedFrame; michael@0: michael@0: SVGSVGElement *ctx = michael@0: static_cast(aMarkedFrame->GetContent())->GetCtx(); michael@0: mFrame->SetParentCoordCtxProvider(ctx); michael@0: } michael@0: michael@0: nsSVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer() michael@0: { michael@0: mFrame->SetParentCoordCtxProvider(nullptr); michael@0: michael@0: mFrame->mMarkedFrame = nullptr; michael@0: mFrame->mInUse = false; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Implementation of nsSVGMarkerAnonChildFrame michael@0: michael@0: nsIFrame* michael@0: NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell, michael@0: nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsSVGMarkerAnonChildFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerAnonChildFrame) michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsSVGMarkerAnonChildFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_ABORT_IF_FALSE(aParent->GetType() == nsGkAtoms::svgMarkerFrame, michael@0: "Unexpected parent"); michael@0: nsSVGMarkerAnonChildFrameBase::Init(aContent, aParent, aPrevInFlow); michael@0: } michael@0: #endif michael@0: michael@0: nsIAtom * michael@0: nsSVGMarkerAnonChildFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::svgMarkerAnonChildFrame; michael@0: }