1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGUseFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,252 @@ 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 +// Keep in (case-insensitive) order: 1.10 +#include "nsIAnonymousContentCreator.h" 1.11 +#include "nsSVGEffects.h" 1.12 +#include "nsSVGGFrame.h" 1.13 +#include "mozilla/dom/SVGUseElement.h" 1.14 +#include "nsContentList.h" 1.15 + 1.16 +typedef nsSVGGFrame nsSVGUseFrameBase; 1.17 + 1.18 +using namespace mozilla::dom; 1.19 + 1.20 +class nsSVGUseFrame : public nsSVGUseFrameBase, 1.21 + public nsIAnonymousContentCreator 1.22 +{ 1.23 + friend nsIFrame* 1.24 + NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); 1.25 + 1.26 +protected: 1.27 + nsSVGUseFrame(nsStyleContext* aContext) : 1.28 + nsSVGUseFrameBase(aContext), 1.29 + mHasValidDimensions(true) 1.30 + {} 1.31 + 1.32 +public: 1.33 + NS_DECL_QUERYFRAME 1.34 + NS_DECL_FRAMEARENA_HELPERS 1.35 + 1.36 + 1.37 + // nsIFrame interface: 1.38 + virtual void Init(nsIContent* aContent, 1.39 + nsIFrame* aParent, 1.40 + nsIFrame* aPrevInFlow) MOZ_OVERRIDE; 1.41 + 1.42 + virtual nsresult AttributeChanged(int32_t aNameSpaceID, 1.43 + nsIAtom* aAttribute, 1.44 + int32_t aModType) MOZ_OVERRIDE; 1.45 + 1.46 + virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; 1.47 + 1.48 + /** 1.49 + * Get the "type" of the frame 1.50 + * 1.51 + * @see nsGkAtoms::svgUseFrame 1.52 + */ 1.53 + virtual nsIAtom* GetType() const MOZ_OVERRIDE; 1.54 + 1.55 + virtual bool IsLeaf() const MOZ_OVERRIDE; 1.56 + 1.57 +#ifdef DEBUG_FRAME_DUMP 1.58 + virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE 1.59 + { 1.60 + return MakeFrameName(NS_LITERAL_STRING("SVGUse"), aResult); 1.61 + } 1.62 +#endif 1.63 + 1.64 + // nsISVGChildFrame interface: 1.65 + virtual void ReflowSVG() MOZ_OVERRIDE; 1.66 + virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE; 1.67 + 1.68 + // nsIAnonymousContentCreator 1.69 + virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) MOZ_OVERRIDE; 1.70 + virtual void AppendAnonymousContentTo(nsBaseContentList& aElements, 1.71 + uint32_t aFilter) MOZ_OVERRIDE; 1.72 + 1.73 +private: 1.74 + bool mHasValidDimensions; 1.75 +}; 1.76 + 1.77 +//---------------------------------------------------------------------- 1.78 +// Implementation 1.79 + 1.80 +nsIFrame* 1.81 +NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.82 +{ 1.83 + return new (aPresShell) nsSVGUseFrame(aContext); 1.84 +} 1.85 + 1.86 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGUseFrame) 1.87 + 1.88 +nsIAtom * 1.89 +nsSVGUseFrame::GetType() const 1.90 +{ 1.91 + return nsGkAtoms::svgUseFrame; 1.92 +} 1.93 + 1.94 +//---------------------------------------------------------------------- 1.95 +// nsQueryFrame methods 1.96 + 1.97 +NS_QUERYFRAME_HEAD(nsSVGUseFrame) 1.98 + NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) 1.99 +NS_QUERYFRAME_TAIL_INHERITING(nsSVGUseFrameBase) 1.100 + 1.101 +//---------------------------------------------------------------------- 1.102 +// nsIFrame methods: 1.103 + 1.104 +void 1.105 +nsSVGUseFrame::Init(nsIContent* aContent, 1.106 + nsIFrame* aParent, 1.107 + nsIFrame* aPrevInFlow) 1.108 +{ 1.109 + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::use), 1.110 + "Content is not an SVG use!"); 1.111 + 1.112 + mHasValidDimensions = 1.113 + static_cast<SVGUseElement*>(aContent)->HasValidDimensions(); 1.114 + 1.115 + nsSVGUseFrameBase::Init(aContent, aParent, aPrevInFlow); 1.116 +} 1.117 + 1.118 +nsresult 1.119 +nsSVGUseFrame::AttributeChanged(int32_t aNameSpaceID, 1.120 + nsIAtom* aAttribute, 1.121 + int32_t aModType) 1.122 +{ 1.123 + SVGUseElement *useElement = static_cast<SVGUseElement*>(mContent); 1.124 + 1.125 + if (aNameSpaceID == kNameSpaceID_None) { 1.126 + if (aAttribute == nsGkAtoms::x || 1.127 + aAttribute == nsGkAtoms::y) { 1.128 + // make sure our cached transform matrix gets (lazily) updated 1.129 + mCanvasTM = nullptr; 1.130 + nsSVGEffects::InvalidateRenderingObservers(this); 1.131 + nsSVGUtils::ScheduleReflowSVG(this); 1.132 + nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED); 1.133 + } else if (aAttribute == nsGkAtoms::width || 1.134 + aAttribute == nsGkAtoms::height) { 1.135 + bool invalidate = false; 1.136 + if (mHasValidDimensions != useElement->HasValidDimensions()) { 1.137 + mHasValidDimensions = !mHasValidDimensions; 1.138 + invalidate = true; 1.139 + } 1.140 + if (useElement->OurWidthAndHeightAreUsed()) { 1.141 + invalidate = true; 1.142 + useElement->SyncWidthOrHeight(aAttribute); 1.143 + } 1.144 + if (invalidate) { 1.145 + nsSVGEffects::InvalidateRenderingObservers(this); 1.146 + nsSVGUtils::ScheduleReflowSVG(this); 1.147 + } 1.148 + } 1.149 + } else if (aNameSpaceID == kNameSpaceID_XLink && 1.150 + aAttribute == nsGkAtoms::href) { 1.151 + // we're changing our nature, clear out the clone information 1.152 + nsSVGEffects::InvalidateRenderingObservers(this); 1.153 + nsSVGUtils::ScheduleReflowSVG(this); 1.154 + useElement->mOriginal = nullptr; 1.155 + useElement->UnlinkSource(); 1.156 + useElement->TriggerReclone(); 1.157 + } 1.158 + 1.159 + return nsSVGUseFrameBase::AttributeChanged(aNameSpaceID, 1.160 + aAttribute, aModType); 1.161 +} 1.162 + 1.163 +void 1.164 +nsSVGUseFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.165 +{ 1.166 + nsRefPtr<SVGUseElement> use = static_cast<SVGUseElement*>(mContent); 1.167 + nsSVGUseFrameBase::DestroyFrom(aDestructRoot); 1.168 + use->DestroyAnonymousContent(); 1.169 +} 1.170 + 1.171 +bool 1.172 +nsSVGUseFrame::IsLeaf() const 1.173 +{ 1.174 + return true; 1.175 +} 1.176 + 1.177 + 1.178 +//---------------------------------------------------------------------- 1.179 +// nsISVGChildFrame methods 1.180 + 1.181 +void 1.182 +nsSVGUseFrame::ReflowSVG() 1.183 +{ 1.184 + // We only handle x/y offset here, since any width/height that is in force is 1.185 + // handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be 1.186 + // created for that purpose. 1.187 + float x, y; 1.188 + static_cast<SVGUseElement*>(mContent)-> 1.189 + GetAnimatedLengthValues(&x, &y, nullptr); 1.190 + mRect.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect( 1.191 + gfxRect(x, y, 0.0, 0.0), 1.192 + PresContext()->AppUnitsPerCSSPixel()).TopLeft()); 1.193 + 1.194 + // If we have a filter, we need to invalidate ourselves because filter 1.195 + // output can change even if none of our descendants need repainting. 1.196 + if (StyleSVGReset()->HasFilters()) { 1.197 + InvalidateFrame(); 1.198 + } 1.199 + 1.200 + nsSVGUseFrameBase::ReflowSVG(); 1.201 +} 1.202 + 1.203 +void 1.204 +nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags) 1.205 +{ 1.206 + if (aFlags & COORD_CONTEXT_CHANGED && 1.207 + !(aFlags & TRANSFORM_CHANGED)) { 1.208 + // Coordinate context changes affect mCanvasTM if we have a 1.209 + // percentage 'x' or 'y' 1.210 + SVGUseElement *use = static_cast<SVGUseElement*>(mContent); 1.211 + if (use->mLengthAttributes[SVGUseElement::ATTR_X].IsPercentage() || 1.212 + use->mLengthAttributes[SVGUseElement::ATTR_Y].IsPercentage()) { 1.213 + aFlags |= TRANSFORM_CHANGED; 1.214 + // Ancestor changes can't affect how we render from the perspective of 1.215 + // any rendering observers that we may have, so we don't need to 1.216 + // invalidate them. We also don't need to invalidate ourself, since our 1.217 + // changed ancestor will have invalidated its entire area, which includes 1.218 + // our area. 1.219 + // For perf reasons we call this before calling NotifySVGChanged() below. 1.220 + nsSVGUtils::ScheduleReflowSVG(this); 1.221 + } 1.222 + } 1.223 + 1.224 + // We don't remove the TRANSFORM_CHANGED flag here if we have a viewBox or 1.225 + // non-percentage width/height, since if they're set then they are cloned to 1.226 + // an anonymous child <svg>, and its nsSVGInnerSVGFrame will do that. 1.227 + 1.228 + nsSVGUseFrameBase::NotifySVGChanged(aFlags); 1.229 +} 1.230 + 1.231 +//---------------------------------------------------------------------- 1.232 +// nsIAnonymousContentCreator methods: 1.233 + 1.234 +nsresult 1.235 +nsSVGUseFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) 1.236 +{ 1.237 + SVGUseElement *use = static_cast<SVGUseElement*>(mContent); 1.238 + 1.239 + nsIContent* clone = use->CreateAnonymousContent(); 1.240 + nsSVGEffects::InvalidateRenderingObservers(this); 1.241 + if (!clone) 1.242 + return NS_ERROR_FAILURE; 1.243 + if (!aElements.AppendElement(clone)) 1.244 + return NS_ERROR_OUT_OF_MEMORY; 1.245 + return NS_OK; 1.246 +} 1.247 + 1.248 +void 1.249 +nsSVGUseFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, 1.250 + uint32_t aFilter) 1.251 +{ 1.252 + SVGUseElement *use = static_cast<SVGUseElement*>(mContent); 1.253 + nsIContent* clone = use->GetAnonymousContent(); 1.254 + aElements.MaybeAppendElement(clone); 1.255 +}