Bug 867089 - Validate the playbackRate before using it. r=ehsan
[gecko.git] / layout / svg / nsSVGContainerFrame.cpp
blobc96ca0d36ccf352f62e4e27338a3d06ace6bf482
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // Main header first:
7 #include "nsSVGContainerFrame.h"
9 // Keep others in (case-insensitive) order:
10 #include "nsCSSFrameConstructor.h"
11 #include "nsSVGEffects.h"
12 #include "nsSVGElement.h"
13 #include "nsSVGUtils.h"
14 #include "nsSVGAnimatedTransformList.h"
16 using namespace mozilla;
18 NS_QUERYFRAME_HEAD(nsSVGContainerFrame)
19 NS_QUERYFRAME_ENTRY(nsSVGContainerFrame)
20 NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrameBase)
22 NS_QUERYFRAME_HEAD(nsSVGDisplayContainerFrame)
23 NS_QUERYFRAME_ENTRY(nsSVGDisplayContainerFrame)
24 NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
25 NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrame)
27 nsIFrame*
28 NS_NewSVGContainerFrame(nsIPresShell* aPresShell,
29 nsStyleContext* aContext)
31 nsIFrame *frame = new (aPresShell) nsSVGContainerFrame(aContext);
32 // If we were called directly, then the frame is for a <defs> or
33 // an unknown element type. In both cases we prevent the content
34 // from displaying directly.
35 frame->AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD);
36 return frame;
39 NS_IMPL_FRAMEARENA_HELPERS(nsSVGContainerFrame)
40 NS_IMPL_FRAMEARENA_HELPERS(nsSVGDisplayContainerFrame)
42 NS_IMETHODIMP
43 nsSVGContainerFrame::AppendFrames(ChildListID aListID,
44 nsFrameList& aFrameList)
46 return InsertFrames(aListID, mFrames.LastChild(), aFrameList);
49 NS_IMETHODIMP
50 nsSVGContainerFrame::InsertFrames(ChildListID aListID,
51 nsIFrame* aPrevFrame,
52 nsFrameList& aFrameList)
54 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
55 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
56 "inserting after sibling frame with different parent");
58 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
60 return NS_OK;
63 NS_IMETHODIMP
64 nsSVGContainerFrame::RemoveFrame(ChildListID aListID,
65 nsIFrame* aOldFrame)
67 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
69 mFrames.DestroyFrame(aOldFrame);
70 return NS_OK;
73 bool
74 nsSVGContainerFrame::UpdateOverflow()
76 if (mState & NS_STATE_SVG_NONDISPLAY_CHILD) {
77 // We don't maintain overflow rects.
78 // XXX It would have be better if the restyle request hadn't even happened.
79 return false;
81 return nsSVGContainerFrameBase::UpdateOverflow();
84 void
85 nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
86 nsIFrame* aParent,
87 nsIFrame* aPrevInFlow)
89 if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
90 AddStateBits(aParent->GetStateBits() &
91 (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD));
93 nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
96 void
97 nsSVGDisplayContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
98 const nsRect& aDirtyRect,
99 const nsDisplayListSet& aLists)
101 if (mContent->IsSVG() &&
102 !static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
103 return;
105 return BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
108 NS_IMETHODIMP
109 nsSVGDisplayContainerFrame::InsertFrames(ChildListID aListID,
110 nsIFrame* aPrevFrame,
111 nsFrameList& aFrameList)
113 // memorize first old frame after insertion point
114 // XXXbz once again, this would work a lot better if the nsIFrame
115 // methods returned framelist iterators....
116 nsIFrame* firstOldFrame = aPrevFrame ?
117 aPrevFrame->GetNextSibling() : GetChildList(aListID).FirstChild();
118 nsIFrame* firstNewFrame = aFrameList.FirstChild();
120 // Insert the new frames
121 nsSVGContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
123 // If we are not a non-display SVG frame and we do not have a bounds update
124 // pending, then we need to schedule one for our new children:
125 if (!(GetStateBits() &
126 (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN |
127 NS_STATE_SVG_NONDISPLAY_CHILD))) {
128 for (nsIFrame* kid = firstNewFrame; kid != firstOldFrame;
129 kid = kid->GetNextSibling()) {
130 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
131 if (SVGFrame) {
132 NS_ABORT_IF_FALSE(!(kid->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
133 "Check for this explicitly in the |if|, then");
134 bool isFirstReflow = (kid->GetStateBits() & NS_FRAME_FIRST_REFLOW);
135 // Remove bits so that ScheduleBoundsUpdate will work:
136 kid->RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
137 NS_FRAME_HAS_DIRTY_CHILDREN);
138 // No need to invalidate the new kid's old bounds, so we just use
139 // nsSVGUtils::ScheduleBoundsUpdate.
140 nsSVGUtils::ScheduleReflowSVG(kid);
141 if (isFirstReflow) {
142 // Add back the NS_FRAME_FIRST_REFLOW bit:
143 kid->AddStateBits(NS_FRAME_FIRST_REFLOW);
149 return NS_OK;
152 NS_IMETHODIMP
153 nsSVGDisplayContainerFrame::RemoveFrame(ChildListID aListID,
154 nsIFrame* aOldFrame)
156 nsSVGEffects::InvalidateRenderingObservers(aOldFrame);
158 // nsSVGContainerFrame::RemoveFrame doesn't call down into
159 // nsContainerFrame::RemoveFrame, so it doesn't call FrameNeedsReflow. We
160 // need to schedule a repaint and schedule an update to our overflow rects.
161 SchedulePaint();
162 PresContext()->PresShell()->FrameConstructor()->PostRestyleEvent(
163 mContent->AsElement(), nsRestyleHint(0), nsChangeHint_UpdateOverflow);
165 nsresult rv = nsSVGContainerFrame::RemoveFrame(aListID, aOldFrame);
167 if (!(GetStateBits() & (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_IS_OUTER_SVG))) {
168 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(this);
171 return rv;
174 bool
175 nsSVGDisplayContainerFrame::IsSVGTransformed(gfxMatrix *aOwnTransform,
176 gfxMatrix *aFromParentTransform) const
178 bool foundTransform = false;
180 // Check if our parent has children-only transforms:
181 nsIFrame *parent = GetParent();
182 if (parent &&
183 parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
184 foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
185 HasChildrenOnlyTransform(aFromParentTransform);
188 if (mContent->IsSVG()) {
189 nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
190 nsSVGAnimatedTransformList* transformList =
191 content->GetAnimatedTransformList();
192 if ((transformList && transformList->HasTransform()) ||
193 content->GetAnimateMotionTransform()) {
194 if (aOwnTransform) {
195 *aOwnTransform = content->PrependLocalTransformsTo(gfxMatrix(),
196 nsSVGElement::eUserSpaceToParent);
198 foundTransform = true;
201 return foundTransform;
204 //----------------------------------------------------------------------
205 // nsISVGChildFrame methods
207 NS_IMETHODIMP
208 nsSVGDisplayContainerFrame::PaintSVG(nsRenderingContext* aContext,
209 const nsIntRect *aDirtyRect)
211 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
212 (mState & NS_STATE_SVG_NONDISPLAY_CHILD) ||
213 PresContext()->IsGlyph(),
214 "If display lists are enabled, only painting of non-display "
215 "SVG should take this code path");
217 const nsStyleDisplay *display = StyleDisplay();
218 if (display->mOpacity == 0.0)
219 return NS_OK;
221 for (nsIFrame* kid = mFrames.FirstChild(); kid;
222 kid = kid->GetNextSibling()) {
223 nsSVGUtils::PaintFrameWithEffects(aContext, aDirtyRect, kid);
226 return NS_OK;
229 NS_IMETHODIMP_(nsIFrame*)
230 nsSVGDisplayContainerFrame::GetFrameForPoint(const nsPoint &aPoint)
232 NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
233 (mState & NS_STATE_SVG_NONDISPLAY_CHILD),
234 "If display lists are enabled, only hit-testing of a "
235 "clipPath's contents should take this code path");
236 return nsSVGUtils::HitTestChildren(this, aPoint);
239 NS_IMETHODIMP_(nsRect)
240 nsSVGDisplayContainerFrame::GetCoveredRegion()
242 return nsSVGUtils::GetCoveredRegion(mFrames);
245 void
246 nsSVGDisplayContainerFrame::ReflowSVG()
248 NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
249 "This call is probably a wasteful mistake");
251 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
252 "ReflowSVG mechanism not designed for this");
254 NS_ABORT_IF_FALSE(GetType() != nsGkAtoms::svgOuterSVGFrame,
255 "Do not call on outer-<svg>");
257 if (!nsSVGUtils::NeedsReflowSVG(this)) {
258 return;
261 // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
262 // then our outer-<svg> has previously had its initial reflow. In that case
263 // we need to make sure that that bit has been removed from ourself _before_
264 // recursing over our children to ensure that they know too. Otherwise, we
265 // need to remove it _after_ recursing over our children so that they know
266 // the initial reflow is currently underway.
268 bool outerSVGHasHadFirstReflow =
269 (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
271 if (outerSVGHasHadFirstReflow) {
272 mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
275 nsOverflowAreas overflowRects;
277 for (nsIFrame* kid = mFrames.FirstChild(); kid;
278 kid = kid->GetNextSibling()) {
279 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
280 if (SVGFrame) {
281 NS_ABORT_IF_FALSE(!(kid->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
282 "Check for this explicitly in the |if|, then");
283 kid->AddStateBits(mState & NS_FRAME_IS_DIRTY);
284 SVGFrame->ReflowSVG();
286 // We build up our child frame overflows here instead of using
287 // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
288 // frame list, and we're iterating over that list now anyway.
289 ConsiderChildOverflow(overflowRects, kid);
293 // <svg> can create an SVG viewport with an offset due to its
294 // x/y/width/height attributes, and <use> can introduce an offset with an
295 // empty mRect (any width/height is copied to an anonymous <svg> child).
296 // Other than that containers should not set mRect since all other offsets
297 // come from transforms, which are accounted for by nsDisplayTransform.
298 // Note that we rely on |overflow:visible| to allow display list items to be
299 // created for our children.
300 NS_ABORT_IF_FALSE(mContent->Tag() == nsGkAtoms::svg ||
301 (mContent->Tag() == nsGkAtoms::use &&
302 mRect.Size() == nsSize(0,0)) ||
303 mRect.IsEqualEdges(nsRect()),
304 "Only inner-<svg>/<use> is expected to have mRect set");
306 if (mState & NS_FRAME_FIRST_REFLOW) {
307 // Make sure we have our filter property (if any) before calling
308 // FinishAndStoreOverflow (subsequent filter changes are handled off
309 // nsChangeHint_UpdateEffects):
310 nsSVGEffects::UpdateEffects(this);
313 FinishAndStoreOverflow(overflowRects, mRect.Size());
315 // Remove state bits after FinishAndStoreOverflow so that it doesn't
316 // invalidate on first reflow:
317 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
318 NS_FRAME_HAS_DIRTY_CHILDREN);
321 void
322 nsSVGDisplayContainerFrame::NotifySVGChanged(uint32_t aFlags)
324 NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
325 "Invalidation logic may need adjusting");
327 nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags);
330 SVGBBox
331 nsSVGDisplayContainerFrame::GetBBoxContribution(
332 const gfxMatrix &aToBBoxUserspace,
333 uint32_t aFlags)
335 SVGBBox bboxUnion;
337 nsIFrame* kid = mFrames.FirstChild();
338 while (kid) {
339 nsISVGChildFrame* svgKid = do_QueryFrame(kid);
340 if (svgKid) {
341 gfxMatrix transform = aToBBoxUserspace;
342 nsIContent *content = kid->GetContent();
343 if (content->IsSVG()) {
344 transform = static_cast<nsSVGElement*>(content)->
345 PrependLocalTransformsTo(aToBBoxUserspace);
347 // We need to include zero width/height vertical/horizontal lines, so we have
348 // to use UnionEdges.
349 bboxUnion.UnionEdges(svgKid->GetBBoxContribution(transform, aFlags));
351 kid = kid->GetNextSibling();
354 return bboxUnion;