1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGInnerSVGFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,315 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Main header first: 1.10 +#include "nsSVGInnerSVGFrame.h" 1.11 + 1.12 +// Keep others in (case-insensitive) order: 1.13 +#include "gfxContext.h" 1.14 +#include "nsIFrame.h" 1.15 +#include "nsISVGChildFrame.h" 1.16 +#include "nsRenderingContext.h" 1.17 +#include "nsSVGContainerFrame.h" 1.18 +#include "nsSVGEffects.h" 1.19 +#include "nsSVGIntegrationUtils.h" 1.20 +#include "mozilla/dom/SVGSVGElement.h" 1.21 + 1.22 +using namespace mozilla; 1.23 +using namespace mozilla::dom; 1.24 + 1.25 +nsIFrame* 1.26 +NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.27 +{ 1.28 + return new (aPresShell) nsSVGInnerSVGFrame(aContext); 1.29 +} 1.30 + 1.31 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGInnerSVGFrame) 1.32 + 1.33 +//---------------------------------------------------------------------- 1.34 +// nsIFrame methods 1.35 + 1.36 +NS_QUERYFRAME_HEAD(nsSVGInnerSVGFrame) 1.37 + NS_QUERYFRAME_ENTRY(nsSVGInnerSVGFrame) 1.38 + NS_QUERYFRAME_ENTRY(nsISVGSVGFrame) 1.39 +NS_QUERYFRAME_TAIL_INHERITING(nsSVGInnerSVGFrameBase) 1.40 + 1.41 +#ifdef DEBUG 1.42 +void 1.43 +nsSVGInnerSVGFrame::Init(nsIContent* aContent, 1.44 + nsIFrame* aParent, 1.45 + nsIFrame* aPrevInFlow) 1.46 +{ 1.47 + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::svg), 1.48 + "Content is not an SVG 'svg' element!"); 1.49 + 1.50 + nsSVGInnerSVGFrameBase::Init(aContent, aParent, aPrevInFlow); 1.51 +} 1.52 +#endif /* DEBUG */ 1.53 + 1.54 +nsIAtom * 1.55 +nsSVGInnerSVGFrame::GetType() const 1.56 +{ 1.57 + return nsGkAtoms::svgInnerSVGFrame; 1.58 +} 1.59 + 1.60 +//---------------------------------------------------------------------- 1.61 +// nsISVGChildFrame methods 1.62 + 1.63 +nsresult 1.64 +nsSVGInnerSVGFrame::PaintSVG(nsRenderingContext *aContext, 1.65 + const nsIntRect *aDirtyRect, 1.66 + nsIFrame* aTransformRoot) 1.67 +{ 1.68 + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || 1.69 + (mState & NS_FRAME_IS_NONDISPLAY), 1.70 + "If display lists are enabled, only painting of non-display " 1.71 + "SVG should take this code path"); 1.72 + 1.73 + gfxContextAutoSaveRestore autoSR; 1.74 + 1.75 + if (StyleDisplay()->IsScrollableOverflow()) { 1.76 + float x, y, width, height; 1.77 + static_cast<SVGSVGElement*>(mContent)-> 1.78 + GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); 1.79 + 1.80 + if (width <= 0 || height <= 0) { 1.81 + return NS_OK; 1.82 + } 1.83 + 1.84 + nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent); 1.85 + gfxMatrix clipTransform = parent->GetCanvasTM(FOR_PAINTING, aTransformRoot); 1.86 + 1.87 + gfxContext *gfx = aContext->ThebesContext(); 1.88 + autoSR.SetContext(gfx); 1.89 + gfxRect clipRect = 1.90 + nsSVGUtils::GetClipRectForFrame(this, x, y, width, height); 1.91 + nsSVGUtils::SetClipRect(gfx, clipTransform, clipRect); 1.92 + } 1.93 + 1.94 + return nsSVGInnerSVGFrameBase::PaintSVG(aContext, aDirtyRect); 1.95 +} 1.96 + 1.97 +void 1.98 +nsSVGInnerSVGFrame::ReflowSVG() 1.99 +{ 1.100 + // mRect must be set before FinishAndStoreOverflow is called in order 1.101 + // for our overflow areas to be clipped correctly. 1.102 + float x, y, width, height; 1.103 + static_cast<SVGSVGElement*>(mContent)-> 1.104 + GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); 1.105 + mRect = nsLayoutUtils::RoundGfxRectToAppRect( 1.106 + gfxRect(x, y, width, height), 1.107 + PresContext()->AppUnitsPerCSSPixel()); 1.108 + 1.109 + // If we have a filter, we need to invalidate ourselves because filter 1.110 + // output can change even if none of our descendants need repainting. 1.111 + if (StyleSVGReset()->HasFilters()) { 1.112 + InvalidateFrame(); 1.113 + } 1.114 + 1.115 + nsSVGInnerSVGFrameBase::ReflowSVG(); 1.116 +} 1.117 + 1.118 +void 1.119 +nsSVGInnerSVGFrame::NotifySVGChanged(uint32_t aFlags) 1.120 +{ 1.121 + NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED), 1.122 + "Invalidation logic may need adjusting"); 1.123 + 1.124 + if (aFlags & COORD_CONTEXT_CHANGED) { 1.125 + 1.126 + SVGSVGElement *svg = static_cast<SVGSVGElement*>(mContent); 1.127 + 1.128 + bool xOrYIsPercentage = 1.129 + svg->mLengthAttributes[SVGSVGElement::ATTR_X].IsPercentage() || 1.130 + svg->mLengthAttributes[SVGSVGElement::ATTR_Y].IsPercentage(); 1.131 + bool widthOrHeightIsPercentage = 1.132 + svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH].IsPercentage() || 1.133 + svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT].IsPercentage(); 1.134 + 1.135 + if (xOrYIsPercentage || widthOrHeightIsPercentage) { 1.136 + // Ancestor changes can't affect how we render from the perspective of 1.137 + // any rendering observers that we may have, so we don't need to 1.138 + // invalidate them. We also don't need to invalidate ourself, since our 1.139 + // changed ancestor will have invalidated its entire area, which includes 1.140 + // our area. 1.141 + // For perf reasons we call this before calling NotifySVGChanged() below. 1.142 + nsSVGUtils::ScheduleReflowSVG(this); 1.143 + } 1.144 + 1.145 + // Coordinate context changes affect mCanvasTM if we have a 1.146 + // percentage 'x' or 'y', or if we have a percentage 'width' or 'height' AND 1.147 + // a 'viewBox'. 1.148 + 1.149 + if (!(aFlags & TRANSFORM_CHANGED) && 1.150 + (xOrYIsPercentage || 1.151 + (widthOrHeightIsPercentage && svg->HasViewBoxRect()))) { 1.152 + aFlags |= TRANSFORM_CHANGED; 1.153 + } 1.154 + 1.155 + if (svg->HasViewBoxRect() || !widthOrHeightIsPercentage) { 1.156 + // Remove COORD_CONTEXT_CHANGED, since we establish the coordinate 1.157 + // context for our descendants and this notification won't change its 1.158 + // dimensions: 1.159 + aFlags &= ~COORD_CONTEXT_CHANGED; 1.160 + 1.161 + if (!aFlags) { 1.162 + return; // No notification flags left 1.163 + } 1.164 + } 1.165 + } 1.166 + 1.167 + if (aFlags & TRANSFORM_CHANGED) { 1.168 + // make sure our cached transform matrix gets (lazily) updated 1.169 + mCanvasTM = nullptr; 1.170 + } 1.171 + 1.172 + nsSVGInnerSVGFrameBase::NotifySVGChanged(aFlags); 1.173 +} 1.174 + 1.175 +nsresult 1.176 +nsSVGInnerSVGFrame::AttributeChanged(int32_t aNameSpaceID, 1.177 + nsIAtom* aAttribute, 1.178 + int32_t aModType) 1.179 +{ 1.180 + if (aNameSpaceID == kNameSpaceID_None && 1.181 + !(GetStateBits() & NS_FRAME_IS_NONDISPLAY)) { 1.182 + 1.183 + SVGSVGElement* content = static_cast<SVGSVGElement*>(mContent); 1.184 + 1.185 + if (aAttribute == nsGkAtoms::width || 1.186 + aAttribute == nsGkAtoms::height) { 1.187 + nsSVGEffects::InvalidateRenderingObservers(this); 1.188 + nsSVGUtils::ScheduleReflowSVG(this); 1.189 + 1.190 + if (content->HasViewBoxOrSyntheticViewBox()) { 1.191 + // make sure our cached transform matrix gets (lazily) updated 1.192 + mCanvasTM = nullptr; 1.193 + content->ChildrenOnlyTransformChanged(); 1.194 + nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED); 1.195 + } else { 1.196 + uint32_t flags = COORD_CONTEXT_CHANGED; 1.197 + if (mCanvasTM && mCanvasTM->IsSingular()) { 1.198 + mCanvasTM = nullptr; 1.199 + flags |= TRANSFORM_CHANGED; 1.200 + } 1.201 + nsSVGUtils::NotifyChildrenOfSVGChange(this, flags); 1.202 + } 1.203 + 1.204 + } else if (aAttribute == nsGkAtoms::transform || 1.205 + aAttribute == nsGkAtoms::preserveAspectRatio || 1.206 + aAttribute == nsGkAtoms::viewBox || 1.207 + aAttribute == nsGkAtoms::x || 1.208 + aAttribute == nsGkAtoms::y) { 1.209 + // make sure our cached transform matrix gets (lazily) updated 1.210 + mCanvasTM = nullptr; 1.211 + 1.212 + nsSVGUtils::NotifyChildrenOfSVGChange( 1.213 + this, aAttribute == nsGkAtoms::viewBox ? 1.214 + TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED); 1.215 + 1.216 + // We don't invalidate for transform changes (the layers code does that). 1.217 + // Also note that SVGTransformableElement::GetAttributeChangeHint will 1.218 + // return nsChangeHint_UpdateOverflow for "transform" attribute changes 1.219 + // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call. 1.220 + 1.221 + if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) { 1.222 + nsSVGEffects::InvalidateRenderingObservers(this); 1.223 + nsSVGUtils::ScheduleReflowSVG(this); 1.224 + } else if (aAttribute == nsGkAtoms::viewBox || 1.225 + (aAttribute == nsGkAtoms::preserveAspectRatio && 1.226 + content->HasViewBoxOrSyntheticViewBox())) { 1.227 + content->ChildrenOnlyTransformChanged(); 1.228 + // SchedulePaint sets a global state flag so we only need to call it once 1.229 + // (on ourself is fine), not once on each child (despite bug 828240). 1.230 + SchedulePaint(); 1.231 + } 1.232 + } 1.233 + } 1.234 + 1.235 + return NS_OK; 1.236 +} 1.237 + 1.238 +nsIFrame* 1.239 +nsSVGInnerSVGFrame::GetFrameForPoint(const nsPoint &aPoint) 1.240 +{ 1.241 + NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || 1.242 + (mState & NS_FRAME_IS_NONDISPLAY), 1.243 + "If display lists are enabled, only hit-testing of non-display " 1.244 + "SVG should take this code path"); 1.245 + 1.246 + if (StyleDisplay()->IsScrollableOverflow()) { 1.247 + nsSVGElement *content = static_cast<nsSVGElement*>(mContent); 1.248 + nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent); 1.249 + 1.250 + float clipX, clipY, clipWidth, clipHeight; 1.251 + content->GetAnimatedLengthValues(&clipX, &clipY, &clipWidth, &clipHeight, nullptr); 1.252 + 1.253 + if (!nsSVGUtils::HitTestRect(gfx::ToMatrix(parent->GetCanvasTM(FOR_HIT_TESTING)), 1.254 + clipX, clipY, clipWidth, clipHeight, 1.255 + PresContext()->AppUnitsToDevPixels(aPoint.x), 1.256 + PresContext()->AppUnitsToDevPixels(aPoint.y))) { 1.257 + return nullptr; 1.258 + } 1.259 + } 1.260 + 1.261 + return nsSVGInnerSVGFrameBase::GetFrameForPoint(aPoint); 1.262 +} 1.263 + 1.264 +//---------------------------------------------------------------------- 1.265 +// nsISVGSVGFrame methods: 1.266 + 1.267 +void 1.268 +nsSVGInnerSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags) 1.269 +{ 1.270 + // The dimensions of inner-<svg> frames are purely defined by their "width" 1.271 + // and "height" attributes, and transform changes can only occur as a result 1.272 + // of changes to their "width", "height", "viewBox" or "preserveAspectRatio" 1.273 + // attributes. Changes to all of these attributes are handled in 1.274 + // AttributeChanged(), so we should never be called. 1.275 + NS_ERROR("Not called for nsSVGInnerSVGFrame"); 1.276 +} 1.277 + 1.278 +//---------------------------------------------------------------------- 1.279 +// nsSVGContainerFrame methods: 1.280 + 1.281 +gfxMatrix 1.282 +nsSVGInnerSVGFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) 1.283 +{ 1.284 + if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) { 1.285 + if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) || 1.286 + (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) { 1.287 + return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this); 1.288 + } 1.289 + } 1.290 + if (!mCanvasTM) { 1.291 + NS_ASSERTION(mParent, "null parent"); 1.292 + 1.293 + nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent); 1.294 + SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent); 1.295 + 1.296 + gfxMatrix tm = content->PrependLocalTransformsTo( 1.297 + this == aTransformRoot ? gfxMatrix() : 1.298 + parent->GetCanvasTM(aFor, aTransformRoot)); 1.299 + 1.300 + mCanvasTM = new gfxMatrix(tm); 1.301 + } 1.302 + return *mCanvasTM; 1.303 +} 1.304 + 1.305 +bool 1.306 +nsSVGInnerSVGFrame::HasChildrenOnlyTransform(gfx::Matrix *aTransform) const 1.307 +{ 1.308 + SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent); 1.309 + 1.310 + if (content->HasViewBoxOrSyntheticViewBox()) { 1.311 + // XXX Maybe return false if the transform is the identity transform? 1.312 + if (aTransform) { 1.313 + *aTransform = content->GetViewBoxTransform(); 1.314 + } 1.315 + return true; 1.316 + } 1.317 + return false; 1.318 +}