/src/mozilla-central/layout/svg/nsSVGContainerFrame.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | // Main header first: |
8 | | #include "nsSVGContainerFrame.h" |
9 | | |
10 | | // Keep others in (case-insensitive) order: |
11 | | #include "ImgDrawResult.h" |
12 | | #include "mozilla/RestyleManager.h" |
13 | | #include "nsCSSFrameConstructor.h" |
14 | | #include "SVGObserverUtils.h" |
15 | | #include "nsSVGElement.h" |
16 | | #include "nsSVGUtils.h" |
17 | | #include "nsSVGAnimatedTransformList.h" |
18 | | #include "SVGTextFrame.h" |
19 | | |
20 | | using namespace mozilla; |
21 | | using namespace mozilla::image; |
22 | | |
23 | 0 | NS_QUERYFRAME_HEAD(nsSVGContainerFrame) |
24 | 0 | NS_QUERYFRAME_ENTRY(nsSVGContainerFrame) |
25 | 0 | NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
26 | | |
27 | 0 | NS_QUERYFRAME_HEAD(nsSVGDisplayContainerFrame) |
28 | 0 | NS_QUERYFRAME_ENTRY(nsSVGDisplayContainerFrame) |
29 | 0 | NS_QUERYFRAME_ENTRY(nsSVGDisplayableFrame) |
30 | 0 | NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrame) |
31 | | |
32 | | nsIFrame* |
33 | | NS_NewSVGContainerFrame(nsIPresShell* aPresShell, |
34 | | ComputedStyle* aStyle) |
35 | 0 | { |
36 | 0 | nsIFrame* frame = |
37 | 0 | new (aPresShell) nsSVGContainerFrame(aStyle, nsSVGContainerFrame::kClassID); |
38 | 0 | // If we were called directly, then the frame is for a <defs> or |
39 | 0 | // an unknown element type. In both cases we prevent the content |
40 | 0 | // from displaying directly. |
41 | 0 | frame->AddStateBits(NS_FRAME_IS_NONDISPLAY); |
42 | 0 | return frame; |
43 | 0 | } |
44 | | |
45 | | NS_IMPL_FRAMEARENA_HELPERS(nsSVGContainerFrame) |
46 | | |
47 | | void |
48 | | nsSVGContainerFrame::AppendFrames(ChildListID aListID, |
49 | | nsFrameList& aFrameList) |
50 | 0 | { |
51 | 0 | InsertFrames(aListID, mFrames.LastChild(), aFrameList); |
52 | 0 | } |
53 | | |
54 | | void |
55 | | nsSVGContainerFrame::InsertFrames(ChildListID aListID, |
56 | | nsIFrame* aPrevFrame, |
57 | | nsFrameList& aFrameList) |
58 | 0 | { |
59 | 0 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
60 | 0 | NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, |
61 | 0 | "inserting after sibling frame with different parent"); |
62 | 0 |
|
63 | 0 | mFrames.InsertFrames(this, aPrevFrame, aFrameList); |
64 | 0 | } |
65 | | |
66 | | void |
67 | | nsSVGContainerFrame::RemoveFrame(ChildListID aListID, |
68 | | nsIFrame* aOldFrame) |
69 | 0 | { |
70 | 0 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
71 | 0 |
|
72 | 0 | mFrames.DestroyFrame(aOldFrame); |
73 | 0 | } |
74 | | |
75 | | bool |
76 | | nsSVGContainerFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) |
77 | 0 | { |
78 | 0 | if (mState & NS_FRAME_IS_NONDISPLAY) { |
79 | 0 | // We don't maintain overflow rects. |
80 | 0 | // XXX It would have be better if the restyle request hadn't even happened. |
81 | 0 | return false; |
82 | 0 | } |
83 | 0 | return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas); |
84 | 0 | } |
85 | | |
86 | | /** |
87 | | * Traverses a frame tree, marking any SVGTextFrame frames as dirty |
88 | | * and calling InvalidateRenderingObservers() on it. |
89 | | * |
90 | | * The reason that this helper exists is because SVGTextFrame is special. |
91 | | * None of the other SVG frames ever need to be reflowed when they have the |
92 | | * NS_FRAME_IS_NONDISPLAY bit set on them because their PaintSVG methods |
93 | | * (and those of any containers that they can validly be contained within) do |
94 | | * not make use of mRect or overflow rects. "em" lengths, etc., are resolved |
95 | | * as those elements are painted. |
96 | | * |
97 | | * SVGTextFrame is different because its anonymous block and inline frames |
98 | | * need to be reflowed in order to get the correct metrics when things like |
99 | | * inherited font-size of an ancestor changes, or a delayed webfont loads and |
100 | | * applies. |
101 | | * |
102 | | * However, we only need to do this work if we were reflowed with |
103 | | * NS_FRAME_IS_DIRTY, which implies that all descendants are dirty. When |
104 | | * that reflow reaches an NS_FRAME_IS_NONDISPLAY frame it would normally |
105 | | * stop, but this helper looks for any SVGTextFrame descendants of such |
106 | | * frames and marks them NS_FRAME_IS_DIRTY so that the next time that they |
107 | | * are painted their anonymous kid will first get the necessary reflow. |
108 | | */ |
109 | | /* static */ void |
110 | | nsSVGContainerFrame::ReflowSVGNonDisplayText(nsIFrame* aContainer) |
111 | 0 | { |
112 | 0 | if (!(aContainer->GetStateBits() & NS_FRAME_IS_DIRTY)) { |
113 | 0 | return; |
114 | 0 | } |
115 | 0 | NS_ASSERTION((aContainer->GetStateBits() & NS_FRAME_IS_NONDISPLAY) || |
116 | 0 | !aContainer->IsFrameOfType(nsIFrame::eSVG), |
117 | 0 | "it is wasteful to call ReflowSVGNonDisplayText on a container " |
118 | 0 | "frame that is not NS_FRAME_IS_NONDISPLAY"); |
119 | 0 | for (nsIFrame* kid : aContainer->PrincipalChildList()) { |
120 | 0 | LayoutFrameType type = kid->Type(); |
121 | 0 | if (type == LayoutFrameType::SVGText) { |
122 | 0 | static_cast<SVGTextFrame*>(kid)->ReflowSVGNonDisplayText(); |
123 | 0 | } else { |
124 | 0 | if (kid->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer) || |
125 | 0 | type == LayoutFrameType::SVGForeignObject || |
126 | 0 | !kid->IsFrameOfType(nsIFrame::eSVG)) { |
127 | 0 | ReflowSVGNonDisplayText(kid); |
128 | 0 | } |
129 | 0 | } |
130 | 0 | } |
131 | 0 | } |
132 | | |
133 | | void |
134 | | nsSVGDisplayContainerFrame::Init(nsIContent* aContent, |
135 | | nsContainerFrame* aParent, |
136 | | nsIFrame* aPrevInFlow) |
137 | 0 | { |
138 | 0 | if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) { |
139 | 0 | AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD); |
140 | 0 | } |
141 | 0 | nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow); |
142 | 0 | } |
143 | | |
144 | | void |
145 | | nsSVGDisplayContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
146 | | const nsDisplayListSet& aLists) |
147 | 0 | { |
148 | 0 | // mContent could be a XUL element so check for an SVG element before casting |
149 | 0 | if (mContent->IsSVGElement() && |
150 | 0 | !static_cast<const nsSVGElement*>(GetContent())->HasValidDimensions()) { |
151 | 0 | return; |
152 | 0 | } |
153 | 0 | DisplayOutline(aBuilder, aLists); |
154 | 0 | return BuildDisplayListForNonBlockChildren(aBuilder, aLists); |
155 | 0 | } |
156 | | |
157 | | void |
158 | | nsSVGDisplayContainerFrame::InsertFrames(ChildListID aListID, |
159 | | nsIFrame* aPrevFrame, |
160 | | nsFrameList& aFrameList) |
161 | 0 | { |
162 | 0 | // memorize first old frame after insertion point |
163 | 0 | // XXXbz once again, this would work a lot better if the nsIFrame |
164 | 0 | // methods returned framelist iterators.... |
165 | 0 | nsIFrame* nextFrame = aPrevFrame ? |
166 | 0 | aPrevFrame->GetNextSibling() : GetChildList(aListID).FirstChild(); |
167 | 0 | nsIFrame* firstNewFrame = aFrameList.FirstChild(); |
168 | 0 |
|
169 | 0 | // Insert the new frames |
170 | 0 | nsSVGContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList); |
171 | 0 |
|
172 | 0 | // If we are not a non-display SVG frame and we do not have a bounds update |
173 | 0 | // pending, then we need to schedule one for our new children: |
174 | 0 | if (!(GetStateBits() & |
175 | 0 | (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN | |
176 | 0 | NS_FRAME_IS_NONDISPLAY))) { |
177 | 0 | for (nsIFrame* kid = firstNewFrame; kid != nextFrame; |
178 | 0 | kid = kid->GetNextSibling()) { |
179 | 0 | nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); |
180 | 0 | if (SVGFrame) { |
181 | 0 | MOZ_ASSERT(!(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY), |
182 | 0 | "Check for this explicitly in the |if|, then"); |
183 | 0 | bool isFirstReflow = (kid->GetStateBits() & NS_FRAME_FIRST_REFLOW); |
184 | 0 | // Remove bits so that ScheduleBoundsUpdate will work: |
185 | 0 | kid->RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | |
186 | 0 | NS_FRAME_HAS_DIRTY_CHILDREN); |
187 | 0 | // No need to invalidate the new kid's old bounds, so we just use |
188 | 0 | // nsSVGUtils::ScheduleBoundsUpdate. |
189 | 0 | nsSVGUtils::ScheduleReflowSVG(kid); |
190 | 0 | if (isFirstReflow) { |
191 | 0 | // Add back the NS_FRAME_FIRST_REFLOW bit: |
192 | 0 | kid->AddStateBits(NS_FRAME_FIRST_REFLOW); |
193 | 0 | } |
194 | 0 | } |
195 | 0 | } |
196 | 0 | } |
197 | 0 | } |
198 | | |
199 | | void |
200 | | nsSVGDisplayContainerFrame::RemoveFrame(ChildListID aListID, |
201 | | nsIFrame* aOldFrame) |
202 | 0 | { |
203 | 0 | SVGObserverUtils::InvalidateRenderingObservers(aOldFrame); |
204 | 0 |
|
205 | 0 | // nsSVGContainerFrame::RemoveFrame doesn't call down into |
206 | 0 | // nsContainerFrame::RemoveFrame, so it doesn't call FrameNeedsReflow. We |
207 | 0 | // need to schedule a repaint and schedule an update to our overflow rects. |
208 | 0 | SchedulePaint(); |
209 | 0 | PresContext()->RestyleManager()->PostRestyleEvent( |
210 | 0 | mContent->AsElement(), nsRestyleHint(0), nsChangeHint_UpdateOverflow); |
211 | 0 |
|
212 | 0 | nsSVGContainerFrame::RemoveFrame(aListID, aOldFrame); |
213 | 0 |
|
214 | 0 | if (!(GetStateBits() & (NS_FRAME_IS_NONDISPLAY | NS_STATE_IS_OUTER_SVG))) { |
215 | 0 | nsSVGUtils::NotifyAncestorsOfFilterRegionChange(this); |
216 | 0 | } |
217 | 0 | } |
218 | | |
219 | | bool |
220 | | nsSVGDisplayContainerFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform, |
221 | | gfx::Matrix *aFromParentTransform) const |
222 | 0 | { |
223 | 0 | bool foundTransform = false; |
224 | 0 |
|
225 | 0 | // Check if our parent has children-only transforms: |
226 | 0 | nsIFrame *parent = GetParent(); |
227 | 0 | if (parent && |
228 | 0 | parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) { |
229 | 0 | foundTransform = static_cast<nsSVGContainerFrame*>(parent)-> |
230 | 0 | HasChildrenOnlyTransform(aFromParentTransform); |
231 | 0 | } |
232 | 0 |
|
233 | 0 | // mContent could be a XUL element so check for an SVG element before casting |
234 | 0 | if (mContent->IsSVGElement()) { |
235 | 0 | nsSVGElement *content = static_cast<nsSVGElement*>(GetContent()); |
236 | 0 | nsSVGAnimatedTransformList* transformList = |
237 | 0 | content->GetAnimatedTransformList(); |
238 | 0 | if ((transformList && transformList->HasTransform()) || |
239 | 0 | content->GetAnimateMotionTransform()) { |
240 | 0 | if (aOwnTransform) { |
241 | 0 | *aOwnTransform = gfx::ToMatrix( |
242 | 0 | content->PrependLocalTransformsTo( |
243 | 0 | gfxMatrix(), eUserSpaceToParent)); |
244 | 0 | } |
245 | 0 | foundTransform = true; |
246 | 0 | } |
247 | 0 | } |
248 | 0 | return foundTransform; |
249 | 0 | } |
250 | | |
251 | | //---------------------------------------------------------------------- |
252 | | // nsSVGDisplayableFrame methods |
253 | | |
254 | | void |
255 | | nsSVGDisplayContainerFrame::PaintSVG(gfxContext& aContext, |
256 | | const gfxMatrix& aTransform, |
257 | | imgDrawingParams& aImgParams, |
258 | | const nsIntRect *aDirtyRect) |
259 | 0 | { |
260 | 0 | NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || |
261 | 0 | (mState & NS_FRAME_IS_NONDISPLAY) || |
262 | 0 | PresContext()->Document()->IsSVGGlyphsDocument(), |
263 | 0 | "If display lists are enabled, only painting of non-display " |
264 | 0 | "SVG should take this code path"); |
265 | 0 |
|
266 | 0 | if (StyleEffects()->mOpacity == 0.0) { |
267 | 0 | return; |
268 | 0 | } |
269 | 0 | |
270 | 0 | gfxMatrix matrix = aTransform; |
271 | 0 | if (GetContent()->IsSVGElement()) { // must check before cast |
272 | 0 | matrix = static_cast<const nsSVGElement*>(GetContent())-> |
273 | 0 | PrependLocalTransformsTo(matrix, eChildToUserSpace); |
274 | 0 | if (matrix.IsSingular()) { |
275 | 0 | return; |
276 | 0 | } |
277 | 0 | } |
278 | 0 | |
279 | 0 | for (nsIFrame* kid = mFrames.FirstChild(); kid; |
280 | 0 | kid = kid->GetNextSibling()) { |
281 | 0 | gfxMatrix m = matrix; |
282 | 0 | // PaintFrameWithEffects() expects the transform that is passed to it to |
283 | 0 | // include the transform to the passed frame's user space, so add it: |
284 | 0 | const nsIContent* content = kid->GetContent(); |
285 | 0 | if (content->IsSVGElement()) { // must check before cast |
286 | 0 | const nsSVGElement* element = static_cast<const nsSVGElement*>(content); |
287 | 0 | if (!element->HasValidDimensions()) { |
288 | 0 | continue; // nothing to paint for kid |
289 | 0 | } |
290 | 0 | m = element->PrependLocalTransformsTo(m, eUserSpaceToParent); |
291 | 0 | if (m.IsSingular()) { |
292 | 0 | continue; |
293 | 0 | } |
294 | 0 | } |
295 | 0 | nsSVGUtils::PaintFrameWithEffects(kid, aContext, m, aImgParams, aDirtyRect); |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | | nsIFrame* |
300 | | nsSVGDisplayContainerFrame::GetFrameForPoint(const gfxPoint& aPoint) |
301 | 0 | { |
302 | 0 | NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || |
303 | 0 | (mState & NS_FRAME_IS_NONDISPLAY), |
304 | 0 | "If display lists are enabled, only hit-testing of a " |
305 | 0 | "clipPath's contents should take this code path"); |
306 | 0 | return nsSVGUtils::HitTestChildren(this, aPoint); |
307 | 0 | } |
308 | | |
309 | | void |
310 | | nsSVGDisplayContainerFrame::ReflowSVG() |
311 | 0 | { |
312 | 0 | NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), |
313 | 0 | "This call is probably a wasteful mistake"); |
314 | 0 |
|
315 | 0 | MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), |
316 | 0 | "ReflowSVG mechanism not designed for this"); |
317 | 0 |
|
318 | 0 | MOZ_ASSERT(!IsSVGOuterSVGFrame(), "Do not call on outer-<svg>"); |
319 | 0 |
|
320 | 0 | if (!nsSVGUtils::NeedsReflowSVG(this)) { |
321 | 0 | return; |
322 | 0 | } |
323 | 0 | |
324 | 0 | // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame, |
325 | 0 | // then our outer-<svg> has previously had its initial reflow. In that case |
326 | 0 | // we need to make sure that that bit has been removed from ourself _before_ |
327 | 0 | // recursing over our children to ensure that they know too. Otherwise, we |
328 | 0 | // need to remove it _after_ recursing over our children so that they know |
329 | 0 | // the initial reflow is currently underway. |
330 | 0 | |
331 | 0 | bool isFirstReflow = (mState & NS_FRAME_FIRST_REFLOW); |
332 | 0 |
|
333 | 0 | bool outerSVGHasHadFirstReflow = |
334 | 0 | (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0; |
335 | 0 |
|
336 | 0 | if (outerSVGHasHadFirstReflow) { |
337 | 0 | RemoveStateBits(NS_FRAME_FIRST_REFLOW); // tell our children |
338 | 0 | } |
339 | 0 |
|
340 | 0 | nsOverflowAreas overflowRects; |
341 | 0 |
|
342 | 0 | for (nsIFrame* kid = mFrames.FirstChild(); kid; |
343 | 0 | kid = kid->GetNextSibling()) { |
344 | 0 | nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); |
345 | 0 | if (SVGFrame) { |
346 | 0 | MOZ_ASSERT(!(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY), |
347 | 0 | "Check for this explicitly in the |if|, then"); |
348 | 0 | kid->AddStateBits(mState & NS_FRAME_IS_DIRTY); |
349 | 0 | SVGFrame->ReflowSVG(); |
350 | 0 |
|
351 | 0 | // We build up our child frame overflows here instead of using |
352 | 0 | // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same |
353 | 0 | // frame list, and we're iterating over that list now anyway. |
354 | 0 | ConsiderChildOverflow(overflowRects, kid); |
355 | 0 | } else { |
356 | 0 | // Inside a non-display container frame, we might have some |
357 | 0 | // SVGTextFrames. We need to cause those to get reflowed in |
358 | 0 | // case they are the target of a rendering observer. |
359 | 0 | NS_ASSERTION(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY, |
360 | 0 | "expected kid to be a NS_FRAME_IS_NONDISPLAY frame"); |
361 | 0 | if (kid->GetStateBits() & NS_FRAME_IS_DIRTY) { |
362 | 0 | nsSVGContainerFrame* container = do_QueryFrame(kid); |
363 | 0 | if (container && container->GetContent()->IsSVGElement()) { |
364 | 0 | ReflowSVGNonDisplayText(container); |
365 | 0 | } |
366 | 0 | } |
367 | 0 | } |
368 | 0 | } |
369 | 0 |
|
370 | 0 | // <svg> can create an SVG viewport with an offset due to its |
371 | 0 | // x/y/width/height attributes, and <use> can introduce an offset with an |
372 | 0 | // empty mRect (any width/height is copied to an anonymous <svg> child). |
373 | 0 | // Other than that containers should not set mRect since all other offsets |
374 | 0 | // come from transforms, which are accounted for by nsDisplayTransform. |
375 | 0 | // Note that we rely on |overflow:visible| to allow display list items to be |
376 | 0 | // created for our children. |
377 | 0 | MOZ_ASSERT(mContent->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol) || |
378 | 0 | (mContent->IsSVGElement(nsGkAtoms::use) && |
379 | 0 | mRect.Size() == nsSize(0,0)) || |
380 | 0 | mRect.IsEqualEdges(nsRect()), |
381 | 0 | "Only inner-<svg>/<use> is expected to have mRect set"); |
382 | 0 |
|
383 | 0 | if (isFirstReflow) { |
384 | 0 | // Make sure we have our filter property (if any) before calling |
385 | 0 | // FinishAndStoreOverflow (subsequent filter changes are handled off |
386 | 0 | // nsChangeHint_UpdateEffects): |
387 | 0 | SVGObserverUtils::UpdateEffects(this); |
388 | 0 | } |
389 | 0 |
|
390 | 0 | FinishAndStoreOverflow(overflowRects, mRect.Size()); |
391 | 0 |
|
392 | 0 | // Remove state bits after FinishAndStoreOverflow so that it doesn't |
393 | 0 | // invalidate on first reflow: |
394 | 0 | RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | |
395 | 0 | NS_FRAME_HAS_DIRTY_CHILDREN); |
396 | 0 | } |
397 | | |
398 | | void |
399 | | nsSVGDisplayContainerFrame::NotifySVGChanged(uint32_t aFlags) |
400 | 0 | { |
401 | 0 | MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED), |
402 | 0 | "Invalidation logic may need adjusting"); |
403 | 0 |
|
404 | 0 | if (aFlags & TRANSFORM_CHANGED) { |
405 | 0 | // make sure our cached transform matrix gets (lazily) updated |
406 | 0 | mCanvasTM = nullptr; |
407 | 0 | } |
408 | 0 |
|
409 | 0 | nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags); |
410 | 0 | } |
411 | | |
412 | | SVGBBox |
413 | | nsSVGDisplayContainerFrame::GetBBoxContribution( |
414 | | const Matrix &aToBBoxUserspace, |
415 | | uint32_t aFlags) |
416 | 0 | { |
417 | 0 | SVGBBox bboxUnion; |
418 | 0 |
|
419 | 0 | nsIFrame* kid = mFrames.FirstChild(); |
420 | 0 | while (kid) { |
421 | 0 | nsIContent *content = kid->GetContent(); |
422 | 0 | nsSVGDisplayableFrame* svgKid = do_QueryFrame(kid); |
423 | 0 | // content could be a XUL element so check for an SVG element before casting |
424 | 0 | if (svgKid && (!content->IsSVGElement() || |
425 | 0 | static_cast<const nsSVGElement*>(content)->HasValidDimensions())) { |
426 | 0 |
|
427 | 0 | gfxMatrix transform = gfx::ThebesMatrix(aToBBoxUserspace); |
428 | 0 | if (content->IsSVGElement()) { |
429 | 0 | transform = static_cast<nsSVGElement*>(content)-> |
430 | 0 | PrependLocalTransformsTo(transform); |
431 | 0 | } |
432 | 0 | // We need to include zero width/height vertical/horizontal lines, so we have |
433 | 0 | // to use UnionEdges. |
434 | 0 | bboxUnion.UnionEdges(svgKid->GetBBoxContribution(gfx::ToMatrix(transform), aFlags)); |
435 | 0 | } |
436 | 0 | kid = kid->GetNextSibling(); |
437 | 0 | } |
438 | 0 |
|
439 | 0 | return bboxUnion; |
440 | 0 | } |
441 | | |
442 | | gfxMatrix |
443 | | nsSVGDisplayContainerFrame::GetCanvasTM() |
444 | 0 | { |
445 | 0 | if (!mCanvasTM) { |
446 | 0 | NS_ASSERTION(GetParent(), "null parent"); |
447 | 0 |
|
448 | 0 | nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent()); |
449 | 0 | nsSVGElement *content = static_cast<nsSVGElement*>(GetContent()); |
450 | 0 |
|
451 | 0 | gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM()); |
452 | 0 |
|
453 | 0 | mCanvasTM = new gfxMatrix(tm); |
454 | 0 | } |
455 | 0 |
|
456 | 0 | return *mCanvasTM; |
457 | 0 | } |