|
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/. */ |
|
5 |
|
6 // Main header first: |
|
7 #include "nsSVGForeignObjectFrame.h" |
|
8 |
|
9 // Keep others in (case-insensitive) order: |
|
10 #include "gfxContext.h" |
|
11 #include "nsGkAtoms.h" |
|
12 #include "nsNameSpaceManager.h" |
|
13 #include "nsLayoutUtils.h" |
|
14 #include "nsRegion.h" |
|
15 #include "nsRenderingContext.h" |
|
16 #include "nsSVGContainerFrame.h" |
|
17 #include "nsSVGEffects.h" |
|
18 #include "mozilla/dom/SVGForeignObjectElement.h" |
|
19 #include "nsSVGIntegrationUtils.h" |
|
20 #include "nsSVGOuterSVGFrame.h" |
|
21 #include "nsSVGUtils.h" |
|
22 #include "mozilla/AutoRestore.h" |
|
23 |
|
24 using namespace mozilla; |
|
25 using namespace mozilla::dom; |
|
26 |
|
27 //---------------------------------------------------------------------- |
|
28 // Implementation |
|
29 |
|
30 nsIFrame* |
|
31 NS_NewSVGForeignObjectFrame(nsIPresShell *aPresShell, |
|
32 nsStyleContext *aContext) |
|
33 { |
|
34 return new (aPresShell) nsSVGForeignObjectFrame(aContext); |
|
35 } |
|
36 |
|
37 NS_IMPL_FRAMEARENA_HELPERS(nsSVGForeignObjectFrame) |
|
38 |
|
39 nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext) |
|
40 : nsSVGForeignObjectFrameBase(aContext), |
|
41 mInReflow(false) |
|
42 { |
|
43 AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED | |
|
44 NS_FRAME_SVG_LAYOUT); |
|
45 } |
|
46 |
|
47 //---------------------------------------------------------------------- |
|
48 // nsIFrame methods |
|
49 |
|
50 NS_QUERYFRAME_HEAD(nsSVGForeignObjectFrame) |
|
51 NS_QUERYFRAME_ENTRY(nsISVGChildFrame) |
|
52 NS_QUERYFRAME_TAIL_INHERITING(nsSVGForeignObjectFrameBase) |
|
53 |
|
54 void |
|
55 nsSVGForeignObjectFrame::Init(nsIContent* aContent, |
|
56 nsIFrame* aParent, |
|
57 nsIFrame* aPrevInFlow) |
|
58 { |
|
59 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::foreignObject), |
|
60 "Content is not an SVG foreignObject!"); |
|
61 |
|
62 nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow); |
|
63 AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD); |
|
64 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER | |
|
65 NS_FRAME_FONT_INFLATION_FLOW_ROOT); |
|
66 if (!(mState & NS_FRAME_IS_NONDISPLAY)) { |
|
67 nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this); |
|
68 } |
|
69 } |
|
70 |
|
71 void nsSVGForeignObjectFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
72 { |
|
73 // Only unregister if we registered in the first place: |
|
74 if (!(mState & NS_FRAME_IS_NONDISPLAY)) { |
|
75 nsSVGUtils::GetOuterSVGFrame(this)->UnregisterForeignObject(this); |
|
76 } |
|
77 nsSVGForeignObjectFrameBase::DestroyFrom(aDestructRoot); |
|
78 } |
|
79 |
|
80 nsIAtom * |
|
81 nsSVGForeignObjectFrame::GetType() const |
|
82 { |
|
83 return nsGkAtoms::svgForeignObjectFrame; |
|
84 } |
|
85 |
|
86 nsresult |
|
87 nsSVGForeignObjectFrame::AttributeChanged(int32_t aNameSpaceID, |
|
88 nsIAtom *aAttribute, |
|
89 int32_t aModType) |
|
90 { |
|
91 if (aNameSpaceID == kNameSpaceID_None) { |
|
92 if (aAttribute == nsGkAtoms::width || |
|
93 aAttribute == nsGkAtoms::height) { |
|
94 nsSVGEffects::InvalidateRenderingObservers(this); |
|
95 nsSVGUtils::ScheduleReflowSVG(this); |
|
96 // XXXjwatt: why mark intrinsic widths dirty? can't we just use eResize? |
|
97 RequestReflow(nsIPresShell::eStyleChange); |
|
98 } else if (aAttribute == nsGkAtoms::x || |
|
99 aAttribute == nsGkAtoms::y) { |
|
100 // make sure our cached transform matrix gets (lazily) updated |
|
101 mCanvasTM = nullptr; |
|
102 nsSVGEffects::InvalidateRenderingObservers(this); |
|
103 nsSVGUtils::ScheduleReflowSVG(this); |
|
104 } else if (aAttribute == nsGkAtoms::transform) { |
|
105 // We don't invalidate for transform changes (the layers code does that). |
|
106 // Also note that SVGTransformableElement::GetAttributeChangeHint will |
|
107 // return nsChangeHint_UpdateOverflow for "transform" attribute changes |
|
108 // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call. |
|
109 mCanvasTM = nullptr; |
|
110 } else if (aAttribute == nsGkAtoms::viewBox || |
|
111 aAttribute == nsGkAtoms::preserveAspectRatio) { |
|
112 nsSVGEffects::InvalidateRenderingObservers(this); |
|
113 } |
|
114 } |
|
115 |
|
116 return NS_OK; |
|
117 } |
|
118 |
|
119 nsresult |
|
120 nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext, |
|
121 nsHTMLReflowMetrics& aDesiredSize, |
|
122 const nsHTMLReflowState& aReflowState, |
|
123 nsReflowStatus& aStatus) |
|
124 { |
|
125 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), |
|
126 "Should not have been called"); |
|
127 |
|
128 // Only InvalidateAndScheduleBoundsUpdate marks us with NS_FRAME_IS_DIRTY, |
|
129 // so if that bit is still set we still have a resize pending. If we hit |
|
130 // this assertion, then we should get the presShell to skip reflow roots |
|
131 // that have a dirty parent since a reflow is going to come via the |
|
132 // reflow root's parent anyway. |
|
133 NS_ASSERTION(!(GetStateBits() & NS_FRAME_IS_DIRTY), |
|
134 "Reflowing while a resize is pending is wasteful"); |
|
135 |
|
136 // ReflowSVG makes sure mRect is up to date before we're called. |
|
137 |
|
138 NS_ASSERTION(!aReflowState.parentReflowState, |
|
139 "should only get reflow from being reflow root"); |
|
140 NS_ASSERTION(aReflowState.ComputedWidth() == GetSize().width && |
|
141 aReflowState.ComputedHeight() == GetSize().height, |
|
142 "reflow roots should be reflowed at existing size and " |
|
143 "svg.css should ensure we have no padding/border/margin"); |
|
144 |
|
145 DoReflow(); |
|
146 |
|
147 aDesiredSize.Width() = aReflowState.ComputedWidth(); |
|
148 aDesiredSize.Height() = aReflowState.ComputedHeight(); |
|
149 aDesiredSize.SetOverflowAreasToDesiredBounds(); |
|
150 aStatus = NS_FRAME_COMPLETE; |
|
151 |
|
152 return NS_OK; |
|
153 } |
|
154 |
|
155 void |
|
156 nsSVGForeignObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
157 const nsRect& aDirtyRect, |
|
158 const nsDisplayListSet& aLists) |
|
159 { |
|
160 if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) { |
|
161 return; |
|
162 } |
|
163 BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists); |
|
164 } |
|
165 |
|
166 bool |
|
167 nsSVGForeignObjectFrame::IsSVGTransformed(Matrix *aOwnTransform, |
|
168 Matrix *aFromParentTransform) const |
|
169 { |
|
170 bool foundTransform = false; |
|
171 |
|
172 // Check if our parent has children-only transforms: |
|
173 nsIFrame *parent = GetParent(); |
|
174 if (parent && |
|
175 parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) { |
|
176 foundTransform = static_cast<nsSVGContainerFrame*>(parent)-> |
|
177 HasChildrenOnlyTransform(aFromParentTransform); |
|
178 } |
|
179 |
|
180 nsSVGElement *content = static_cast<nsSVGElement*>(mContent); |
|
181 nsSVGAnimatedTransformList* transformList = |
|
182 content->GetAnimatedTransformList(); |
|
183 if ((transformList && transformList->HasTransform()) || |
|
184 content->GetAnimateMotionTransform()) { |
|
185 if (aOwnTransform) { |
|
186 *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(), |
|
187 nsSVGElement::eUserSpaceToParent)); |
|
188 } |
|
189 foundTransform = true; |
|
190 } |
|
191 return foundTransform; |
|
192 } |
|
193 |
|
194 nsresult |
|
195 nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext, |
|
196 const nsIntRect *aDirtyRect, |
|
197 nsIFrame* aTransformRoot) |
|
198 { |
|
199 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || |
|
200 (mState & NS_FRAME_IS_NONDISPLAY), |
|
201 "If display lists are enabled, only painting of non-display " |
|
202 "SVG should take this code path"); |
|
203 |
|
204 if (IsDisabled()) |
|
205 return NS_OK; |
|
206 |
|
207 nsIFrame* kid = GetFirstPrincipalChild(); |
|
208 if (!kid) |
|
209 return NS_OK; |
|
210 |
|
211 gfxMatrix canvasTM = GetCanvasTM(FOR_PAINTING, aTransformRoot); |
|
212 |
|
213 if (canvasTM.IsSingular()) { |
|
214 NS_WARNING("Can't render foreignObject element!"); |
|
215 return NS_ERROR_FAILURE; |
|
216 } |
|
217 |
|
218 nsRect kidDirtyRect = kid->GetVisualOverflowRect(); |
|
219 |
|
220 /* Check if we need to draw anything. */ |
|
221 if (aDirtyRect) { |
|
222 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || |
|
223 (mState & NS_FRAME_IS_NONDISPLAY), |
|
224 "Display lists handle dirty rect intersection test"); |
|
225 // Transform the dirty rect into app units in our userspace. |
|
226 gfxMatrix invmatrix = canvasTM; |
|
227 invmatrix.Invert(); |
|
228 NS_ASSERTION(!invmatrix.IsSingular(), |
|
229 "inverse of non-singular matrix should be non-singular"); |
|
230 |
|
231 gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y, |
|
232 aDirtyRect->width, aDirtyRect->height); |
|
233 transDirtyRect = invmatrix.TransformBounds(transDirtyRect); |
|
234 |
|
235 kidDirtyRect.IntersectRect(kidDirtyRect, |
|
236 nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect, |
|
237 PresContext()->AppUnitsPerCSSPixel())); |
|
238 |
|
239 // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect, |
|
240 // not with kidDirtyRect. I.e. |
|
241 // int32_t appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); |
|
242 // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect) |
|
243 if (kidDirtyRect.IsEmpty()) |
|
244 return NS_OK; |
|
245 } |
|
246 |
|
247 gfxContext *gfx = aContext->ThebesContext(); |
|
248 |
|
249 gfx->Save(); |
|
250 |
|
251 if (StyleDisplay()->IsScrollableOverflow()) { |
|
252 float x, y, width, height; |
|
253 static_cast<nsSVGElement*>(mContent)-> |
|
254 GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
|
255 |
|
256 gfxRect clipRect = |
|
257 nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height); |
|
258 nsSVGUtils::SetClipRect(gfx, canvasTM, clipRect); |
|
259 } |
|
260 |
|
261 // SVG paints in CSS px, but normally frames paint in dev pixels. Here we |
|
262 // multiply a CSS-px-to-dev-pixel factor onto canvasTM so our children paint |
|
263 // correctly. |
|
264 float cssPxPerDevPx = PresContext()-> |
|
265 AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel()); |
|
266 gfxMatrix canvasTMForChildren = canvasTM; |
|
267 canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx); |
|
268 |
|
269 gfx->Multiply(canvasTMForChildren); |
|
270 |
|
271 uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM; |
|
272 if (SVGAutoRenderState::IsPaintingToWindow(aContext)) { |
|
273 flags |= nsLayoutUtils::PAINT_TO_WINDOW; |
|
274 } |
|
275 nsresult rv = nsLayoutUtils::PaintFrame(aContext, kid, nsRegion(kidDirtyRect), |
|
276 NS_RGBA(0,0,0,0), flags); |
|
277 |
|
278 gfx->Restore(); |
|
279 |
|
280 return rv; |
|
281 } |
|
282 |
|
283 nsIFrame* |
|
284 nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint) |
|
285 { |
|
286 NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || |
|
287 (mState & NS_FRAME_IS_NONDISPLAY), |
|
288 "If display lists are enabled, only hit-testing of a " |
|
289 "clipPath's contents should take this code path"); |
|
290 |
|
291 if (IsDisabled() || (GetStateBits() & NS_FRAME_IS_NONDISPLAY)) |
|
292 return nullptr; |
|
293 |
|
294 nsIFrame* kid = GetFirstPrincipalChild(); |
|
295 if (!kid) |
|
296 return nullptr; |
|
297 |
|
298 float x, y, width, height; |
|
299 static_cast<nsSVGElement*>(mContent)-> |
|
300 GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
|
301 |
|
302 gfxMatrix tm = GetCanvasTM(FOR_HIT_TESTING).Invert(); |
|
303 if (tm.IsSingular()) |
|
304 return nullptr; |
|
305 |
|
306 // Convert aPoint from app units in canvas space to user space: |
|
307 |
|
308 gfxPoint pt = gfxPoint(aPoint.x, aPoint.y) / PresContext()->AppUnitsPerDevPixel(); |
|
309 pt = tm.Transform(pt); |
|
310 |
|
311 if (!gfxRect(0.0f, 0.0f, width, height).Contains(pt)) |
|
312 return nullptr; |
|
313 |
|
314 // Convert pt to app units in *local* space: |
|
315 |
|
316 pt = pt * nsPresContext::AppUnitsPerCSSPixel(); |
|
317 nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y)); |
|
318 |
|
319 nsIFrame *frame = nsLayoutUtils::GetFrameForPoint(kid, point); |
|
320 if (frame && nsSVGUtils::HitTestClip(this, aPoint)) |
|
321 return frame; |
|
322 |
|
323 return nullptr; |
|
324 } |
|
325 |
|
326 nsRect |
|
327 nsSVGForeignObjectFrame::GetCoveredRegion() |
|
328 { |
|
329 float x, y, w, h; |
|
330 static_cast<SVGForeignObjectElement*>(mContent)-> |
|
331 GetAnimatedLengthValues(&x, &y, &w, &h, nullptr); |
|
332 if (w < 0.0f) w = 0.0f; |
|
333 if (h < 0.0f) h = 0.0f; |
|
334 // GetCanvasTM includes the x,y translation |
|
335 return nsSVGUtils::ToCanvasBounds(gfxRect(0.0, 0.0, w, h), |
|
336 GetCanvasTM(FOR_OUTERSVG_TM), |
|
337 PresContext()); |
|
338 } |
|
339 |
|
340 void |
|
341 nsSVGForeignObjectFrame::ReflowSVG() |
|
342 { |
|
343 NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), |
|
344 "This call is probably a wasteful mistake"); |
|
345 |
|
346 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), |
|
347 "ReflowSVG mechanism not designed for this"); |
|
348 |
|
349 if (!nsSVGUtils::NeedsReflowSVG(this)) { |
|
350 return; |
|
351 } |
|
352 |
|
353 // We update mRect before the DoReflow call so that DoReflow uses the |
|
354 // correct dimensions: |
|
355 |
|
356 float x, y, w, h; |
|
357 static_cast<SVGForeignObjectElement*>(mContent)-> |
|
358 GetAnimatedLengthValues(&x, &y, &w, &h, nullptr); |
|
359 |
|
360 // If mRect's width or height are negative, reflow blows up! We must clamp! |
|
361 if (w < 0.0f) w = 0.0f; |
|
362 if (h < 0.0f) h = 0.0f; |
|
363 |
|
364 mRect = nsLayoutUtils::RoundGfxRectToAppRect( |
|
365 gfxRect(x, y, w, h), |
|
366 PresContext()->AppUnitsPerCSSPixel()); |
|
367 |
|
368 // Fully mark our kid dirty so that it gets resized if necessary |
|
369 // (NS_FRAME_HAS_DIRTY_CHILDREN isn't enough in that case): |
|
370 nsIFrame* kid = GetFirstPrincipalChild(); |
|
371 kid->AddStateBits(NS_FRAME_IS_DIRTY); |
|
372 |
|
373 // Make sure to not allow interrupts if we're not being reflown as a root: |
|
374 nsPresContext::InterruptPreventer noInterrupts(PresContext()); |
|
375 |
|
376 DoReflow(); |
|
377 |
|
378 if (mState & NS_FRAME_FIRST_REFLOW) { |
|
379 // Make sure we have our filter property (if any) before calling |
|
380 // FinishAndStoreOverflow (subsequent filter changes are handled off |
|
381 // nsChangeHint_UpdateEffects): |
|
382 nsSVGEffects::UpdateEffects(this); |
|
383 } |
|
384 |
|
385 // If we have a filter, we need to invalidate ourselves because filter |
|
386 // output can change even if none of our descendants need repainting. |
|
387 if (StyleSVGReset()->HasFilters()) { |
|
388 InvalidateFrame(); |
|
389 } |
|
390 |
|
391 // TODO: once we support |overflow:visible| on foreignObject, then we will |
|
392 // need to take account of our descendants here. |
|
393 nsRect overflow = nsRect(nsPoint(0,0), mRect.Size()); |
|
394 nsOverflowAreas overflowAreas(overflow, overflow); |
|
395 FinishAndStoreOverflow(overflowAreas, mRect.Size()); |
|
396 |
|
397 // Now unset the various reflow bits: |
|
398 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | |
|
399 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
400 } |
|
401 |
|
402 void |
|
403 nsSVGForeignObjectFrame::NotifySVGChanged(uint32_t aFlags) |
|
404 { |
|
405 NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED), |
|
406 "Invalidation logic may need adjusting"); |
|
407 |
|
408 bool needNewBounds = false; // i.e. mRect or visual overflow rect |
|
409 bool needReflow = false; |
|
410 bool needNewCanvasTM = false; |
|
411 |
|
412 if (aFlags & COORD_CONTEXT_CHANGED) { |
|
413 SVGForeignObjectElement *fO = |
|
414 static_cast<SVGForeignObjectElement*>(mContent); |
|
415 // Coordinate context changes affect mCanvasTM if we have a |
|
416 // percentage 'x' or 'y' |
|
417 if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_X].IsPercentage() || |
|
418 fO->mLengthAttributes[SVGForeignObjectElement::ATTR_Y].IsPercentage()) { |
|
419 needNewBounds = true; |
|
420 needNewCanvasTM = true; |
|
421 } |
|
422 // Our coordinate context's width/height has changed. If we have a |
|
423 // percentage width/height our dimensions will change so we must reflow. |
|
424 if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_WIDTH].IsPercentage() || |
|
425 fO->mLengthAttributes[SVGForeignObjectElement::ATTR_HEIGHT].IsPercentage()) { |
|
426 needNewBounds = true; |
|
427 needReflow = true; |
|
428 } |
|
429 } |
|
430 |
|
431 if (aFlags & TRANSFORM_CHANGED) { |
|
432 if (mCanvasTM && mCanvasTM->IsSingular()) { |
|
433 needNewBounds = true; // old bounds are bogus |
|
434 } |
|
435 needNewCanvasTM = true; |
|
436 // In an ideal world we would reflow when our CTM changes. This is because |
|
437 // glyph metrics do not necessarily scale uniformly with change in scale |
|
438 // and, as a result, CTM changes may require text to break at different |
|
439 // points. The problem would be how to keep performance acceptable when |
|
440 // e.g. the transform of an ancestor is animated. |
|
441 // We also seem to get some sort of infinite loop post bug 421584 if we |
|
442 // reflow. |
|
443 } |
|
444 |
|
445 if (needNewBounds) { |
|
446 // Ancestor changes can't affect how we render from the perspective of |
|
447 // any rendering observers that we may have, so we don't need to |
|
448 // invalidate them. We also don't need to invalidate ourself, since our |
|
449 // changed ancestor will have invalidated its entire area, which includes |
|
450 // our area. |
|
451 nsSVGUtils::ScheduleReflowSVG(this); |
|
452 } |
|
453 |
|
454 // If we're called while the PresShell is handling reflow events then we |
|
455 // must have been called as a result of the NotifyViewportChange() call in |
|
456 // our nsSVGOuterSVGFrame's Reflow() method. We must not call RequestReflow |
|
457 // at this point (i.e. during reflow) because it could confuse the |
|
458 // PresShell and prevent it from reflowing us properly in future. Besides |
|
459 // that, nsSVGOuterSVGFrame::DidReflow will take care of reflowing us |
|
460 // synchronously, so there's no need. |
|
461 if (needReflow && !PresContext()->PresShell()->IsReflowLocked()) { |
|
462 RequestReflow(nsIPresShell::eResize); |
|
463 } |
|
464 |
|
465 if (needNewCanvasTM) { |
|
466 // Do this after calling InvalidateAndScheduleBoundsUpdate in case we |
|
467 // change the code and it needs to use it. |
|
468 mCanvasTM = nullptr; |
|
469 } |
|
470 } |
|
471 |
|
472 SVGBBox |
|
473 nsSVGForeignObjectFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace, |
|
474 uint32_t aFlags) |
|
475 { |
|
476 SVGForeignObjectElement *content = |
|
477 static_cast<SVGForeignObjectElement*>(mContent); |
|
478 |
|
479 float x, y, w, h; |
|
480 content->GetAnimatedLengthValues(&x, &y, &w, &h, nullptr); |
|
481 |
|
482 if (w < 0.0f) w = 0.0f; |
|
483 if (h < 0.0f) h = 0.0f; |
|
484 |
|
485 if (aToBBoxUserspace.IsSingular()) { |
|
486 // XXX ReportToConsole |
|
487 return SVGBBox(); |
|
488 } |
|
489 return aToBBoxUserspace.TransformBounds(gfx::Rect(0.0, 0.0, w, h)); |
|
490 } |
|
491 |
|
492 //---------------------------------------------------------------------- |
|
493 |
|
494 gfxMatrix |
|
495 nsSVGForeignObjectFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) |
|
496 { |
|
497 if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) { |
|
498 if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) || |
|
499 (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) { |
|
500 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this); |
|
501 } |
|
502 } |
|
503 if (!mCanvasTM) { |
|
504 NS_ASSERTION(mParent, "null parent"); |
|
505 |
|
506 nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent); |
|
507 SVGForeignObjectElement *content = |
|
508 static_cast<SVGForeignObjectElement*>(mContent); |
|
509 |
|
510 gfxMatrix tm = content->PrependLocalTransformsTo( |
|
511 this == aTransformRoot ? gfxMatrix() : |
|
512 parent->GetCanvasTM(aFor, aTransformRoot)); |
|
513 |
|
514 mCanvasTM = new gfxMatrix(tm); |
|
515 } |
|
516 return *mCanvasTM; |
|
517 } |
|
518 |
|
519 //---------------------------------------------------------------------- |
|
520 // Implementation helpers |
|
521 |
|
522 void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType) |
|
523 { |
|
524 if (GetStateBits() & NS_FRAME_FIRST_REFLOW) |
|
525 // If we haven't had a ReflowSVG() yet, nothing to do. |
|
526 return; |
|
527 |
|
528 nsIFrame* kid = GetFirstPrincipalChild(); |
|
529 if (!kid) |
|
530 return; |
|
531 |
|
532 PresContext()->PresShell()->FrameNeedsReflow(kid, aType, NS_FRAME_IS_DIRTY); |
|
533 } |
|
534 |
|
535 void |
|
536 nsSVGForeignObjectFrame::DoReflow() |
|
537 { |
|
538 // Skip reflow if we're zero-sized, unless this is our first reflow. |
|
539 if (IsDisabled() && |
|
540 !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) |
|
541 return; |
|
542 |
|
543 nsPresContext *presContext = PresContext(); |
|
544 nsIFrame* kid = GetFirstPrincipalChild(); |
|
545 if (!kid) |
|
546 return; |
|
547 |
|
548 // initiate a synchronous reflow here and now: |
|
549 nsRefPtr<nsRenderingContext> renderingContext = |
|
550 presContext->PresShell()->CreateReferenceRenderingContext(); |
|
551 |
|
552 mInReflow = true; |
|
553 |
|
554 nsHTMLReflowState reflowState(presContext, kid, |
|
555 renderingContext, |
|
556 nsSize(mRect.width, NS_UNCONSTRAINEDSIZE)); |
|
557 nsHTMLReflowMetrics desiredSize(reflowState); |
|
558 nsReflowStatus status; |
|
559 |
|
560 // We don't use mRect.height above because that tells the child to do |
|
561 // page/column breaking at that height. |
|
562 NS_ASSERTION(reflowState.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) && |
|
563 reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0), |
|
564 "style system should ensure that :-moz-svg-foreign-content " |
|
565 "does not get styled"); |
|
566 NS_ASSERTION(reflowState.ComputedWidth() == mRect.width, |
|
567 "reflow state made child wrong size"); |
|
568 reflowState.SetComputedHeight(mRect.height); |
|
569 |
|
570 ReflowChild(kid, presContext, desiredSize, reflowState, 0, 0, |
|
571 NS_FRAME_NO_MOVE_FRAME, status); |
|
572 NS_ASSERTION(mRect.width == desiredSize.Width() && |
|
573 mRect.height == desiredSize.Height(), "unexpected size"); |
|
574 FinishReflowChild(kid, presContext, desiredSize, &reflowState, 0, 0, |
|
575 NS_FRAME_NO_MOVE_FRAME); |
|
576 |
|
577 mInReflow = false; |
|
578 } |
|
579 |
|
580 nsRect |
|
581 nsSVGForeignObjectFrame::GetInvalidRegion() |
|
582 { |
|
583 nsIFrame* kid = GetFirstPrincipalChild(); |
|
584 if (kid->HasInvalidFrameInSubtree()) { |
|
585 gfxRect r(mRect.x, mRect.y, mRect.width, mRect.height); |
|
586 r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel()); |
|
587 nsRect rect = nsSVGUtils::ToCanvasBounds(r, GetCanvasTM(FOR_PAINTING), PresContext()); |
|
588 rect = nsSVGUtils::GetPostFilterVisualOverflowRect(this, rect); |
|
589 return rect; |
|
590 } |
|
591 return nsRect(); |
|
592 } |
|
593 |
|
594 |