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 "DrawResult.h"
11 #include "gfxContext.h"
12 #include "nsDisplayList.h"
13 #include "nsGkAtoms.h"
14 #include "nsNameSpaceManager.h"
15 #include "nsLayoutUtils.h"
16 #include "nsRegion.h"
17 #include "nsRenderingContext.h"
18 #include "nsSVGContainerFrame.h"
19 #include "nsSVGEffects.h"
20 #include "mozilla/dom/SVGForeignObjectElement.h"
21 #include "nsSVGIntegrationUtils.h"
22 #include "nsSVGOuterSVGFrame.h"
23 #include "nsSVGUtils.h"
24 #include "mozilla/AutoRestore.h"
25 
26 using namespace mozilla;
27 using namespace mozilla::dom;
28 using namespace mozilla::image;
29 
30 //----------------------------------------------------------------------
31 // Implementation
32 
33 nsContainerFrame*
NS_NewSVGForeignObjectFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)34 NS_NewSVGForeignObjectFrame(nsIPresShell   *aPresShell,
35                             nsStyleContext *aContext)
36 {
37   return new (aPresShell) nsSVGForeignObjectFrame(aContext);
38 }
39 
NS_IMPL_FRAMEARENA_HELPERS(nsSVGForeignObjectFrame)40 NS_IMPL_FRAMEARENA_HELPERS(nsSVGForeignObjectFrame)
41 
42 nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext)
43   : nsContainerFrame(aContext)
44   , mInReflow(false)
45 {
46   AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED |
47                NS_FRAME_SVG_LAYOUT);
48 }
49 
50 //----------------------------------------------------------------------
51 // nsIFrame methods
52 
53 NS_QUERYFRAME_HEAD(nsSVGForeignObjectFrame)
NS_QUERYFRAME_ENTRY(nsISVGChildFrame)54   NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
55 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
56 
57 void
58 nsSVGForeignObjectFrame::Init(nsIContent*       aContent,
59                               nsContainerFrame* aParent,
60                               nsIFrame*         aPrevInFlow)
61 {
62   NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::foreignObject),
63                "Content is not an SVG foreignObject!");
64 
65   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
66   AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
67   AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER |
68                NS_FRAME_FONT_INFLATION_FLOW_ROOT);
69   if (!(mState & NS_FRAME_IS_NONDISPLAY)) {
70     nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this);
71   }
72 }
73 
DestroyFrom(nsIFrame * aDestructRoot)74 void nsSVGForeignObjectFrame::DestroyFrom(nsIFrame* aDestructRoot)
75 {
76   // Only unregister if we registered in the first place:
77   if (!(mState & NS_FRAME_IS_NONDISPLAY)) {
78       nsSVGUtils::GetOuterSVGFrame(this)->UnregisterForeignObject(this);
79   }
80   nsContainerFrame::DestroyFrom(aDestructRoot);
81 }
82 
83 nsIAtom *
GetType() const84 nsSVGForeignObjectFrame::GetType() const
85 {
86   return nsGkAtoms::svgForeignObjectFrame;
87 }
88 
89 nsresult
AttributeChanged(int32_t aNameSpaceID,nsIAtom * aAttribute,int32_t aModType)90 nsSVGForeignObjectFrame::AttributeChanged(int32_t  aNameSpaceID,
91                                           nsIAtom *aAttribute,
92                                           int32_t  aModType)
93 {
94   if (aNameSpaceID == kNameSpaceID_None) {
95     if (aAttribute == nsGkAtoms::width ||
96         aAttribute == nsGkAtoms::height) {
97       nsLayoutUtils::PostRestyleEvent(
98         mContent->AsElement(), nsRestyleHint(0),
99         nsChangeHint_InvalidateRenderingObservers);
100       nsSVGUtils::ScheduleReflowSVG(this);
101       // XXXjwatt: why mark intrinsic widths dirty? can't we just use eResize?
102       RequestReflow(nsIPresShell::eStyleChange);
103     } else if (aAttribute == nsGkAtoms::x ||
104                aAttribute == nsGkAtoms::y) {
105       // make sure our cached transform matrix gets (lazily) updated
106       mCanvasTM = nullptr;
107       nsLayoutUtils::PostRestyleEvent(
108         mContent->AsElement(), nsRestyleHint(0),
109         nsChangeHint_InvalidateRenderingObservers);
110       nsSVGUtils::ScheduleReflowSVG(this);
111     } else if (aAttribute == nsGkAtoms::transform) {
112       // We don't invalidate for transform changes (the layers code does that).
113       // Also note that SVGTransformableElement::GetAttributeChangeHint will
114       // return nsChangeHint_UpdateOverflow for "transform" attribute changes
115       // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
116       mCanvasTM = nullptr;
117     } else if (aAttribute == nsGkAtoms::viewBox ||
118                aAttribute == nsGkAtoms::preserveAspectRatio) {
119       nsLayoutUtils::PostRestyleEvent(
120         mContent->AsElement(), nsRestyleHint(0),
121         nsChangeHint_InvalidateRenderingObservers);
122     }
123   }
124 
125   return NS_OK;
126 }
127 
128 void
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)129 nsSVGForeignObjectFrame::Reflow(nsPresContext*           aPresContext,
130                                 ReflowOutput&     aDesiredSize,
131                                 const ReflowInput& aReflowInput,
132                                 nsReflowStatus&          aStatus)
133 {
134   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
135              "Should not have been called");
136 
137   // Only InvalidateAndScheduleBoundsUpdate marks us with NS_FRAME_IS_DIRTY,
138   // so if that bit is still set we still have a resize pending. If we hit
139   // this assertion, then we should get the presShell to skip reflow roots
140   // that have a dirty parent since a reflow is going to come via the
141   // reflow root's parent anyway.
142   NS_ASSERTION(!(GetStateBits() & NS_FRAME_IS_DIRTY),
143                "Reflowing while a resize is pending is wasteful");
144 
145   // ReflowSVG makes sure mRect is up to date before we're called.
146 
147   NS_ASSERTION(!aReflowInput.mParentReflowInput,
148                "should only get reflow from being reflow root");
149   NS_ASSERTION(aReflowInput.ComputedWidth() == GetSize().width &&
150                aReflowInput.ComputedHeight() == GetSize().height,
151                "reflow roots should be reflowed at existing size and "
152                "svg.css should ensure we have no padding/border/margin");
153 
154   DoReflow();
155 
156   WritingMode wm = aReflowInput.GetWritingMode();
157   LogicalSize finalSize(wm, aReflowInput.ComputedISize(),
158                         aReflowInput.ComputedBSize());
159   aDesiredSize.SetSize(wm, finalSize);
160   aDesiredSize.SetOverflowAreasToDesiredBounds();
161   aStatus = NS_FRAME_COMPLETE;
162 }
163 
164 void
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsRect & aDirtyRect,const nsDisplayListSet & aLists)165 nsSVGForeignObjectFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
166                                           const nsRect&           aDirtyRect,
167                                           const nsDisplayListSet& aLists)
168 {
169   if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
170     return;
171   }
172   DisplayOutline(aBuilder, aLists);
173   BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
174 }
175 
176 bool
IsSVGTransformed(Matrix * aOwnTransform,Matrix * aFromParentTransform) const177 nsSVGForeignObjectFrame::IsSVGTransformed(Matrix *aOwnTransform,
178                                           Matrix *aFromParentTransform) const
179 {
180   bool foundTransform = false;
181 
182   // Check if our parent has children-only transforms:
183   nsIFrame *parent = GetParent();
184   if (parent &&
185       parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
186     foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
187                        HasChildrenOnlyTransform(aFromParentTransform);
188   }
189 
190   nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
191   nsSVGAnimatedTransformList* transformList =
192     content->GetAnimatedTransformList();
193   if ((transformList && transformList->HasTransform()) ||
194       content->GetAnimateMotionTransform()) {
195     if (aOwnTransform) {
196       *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(
197                                        gfxMatrix(),
198                                        eUserSpaceToParent));
199     }
200     foundTransform = true;
201   }
202   return foundTransform;
203 }
204 
205 DrawResult
PaintSVG(gfxContext & aContext,const gfxMatrix & aTransform,const nsIntRect * aDirtyRect)206 nsSVGForeignObjectFrame::PaintSVG(gfxContext& aContext,
207                                   const gfxMatrix& aTransform,
208                                   const nsIntRect* aDirtyRect)
209 {
210   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
211                (mState & NS_FRAME_IS_NONDISPLAY),
212                "If display lists are enabled, only painting of non-display "
213                "SVG should take this code path");
214 
215   if (IsDisabled())
216     return DrawResult::SUCCESS;
217 
218   nsIFrame* kid = PrincipalChildList().FirstChild();
219   if (!kid)
220     return DrawResult::SUCCESS;
221 
222   if (aTransform.IsSingular()) {
223     NS_WARNING("Can't render foreignObject element!");
224     return DrawResult::BAD_ARGS;
225   }
226 
227   nsRect kidDirtyRect = kid->GetVisualOverflowRect();
228 
229   /* Check if we need to draw anything. */
230   if (aDirtyRect) {
231     NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
232                  (mState & NS_FRAME_IS_NONDISPLAY),
233                  "Display lists handle dirty rect intersection test");
234     // Transform the dirty rect into app units in our userspace.
235     gfxMatrix invmatrix = aTransform;
236     DebugOnly<bool> ok = invmatrix.Invert();
237     NS_ASSERTION(ok, "inverse of non-singular matrix should be non-singular");
238 
239     gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y,
240                                      aDirtyRect->width, aDirtyRect->height);
241     transDirtyRect = invmatrix.TransformBounds(transDirtyRect);
242 
243     kidDirtyRect.IntersectRect(kidDirtyRect,
244       nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect,
245                        PresContext()->AppUnitsPerCSSPixel()));
246 
247     // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect,
248     // not with kidDirtyRect. I.e.
249     // int32_t appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
250     // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect)
251     if (kidDirtyRect.IsEmpty())
252       return DrawResult::SUCCESS;
253   }
254 
255   aContext.Save();
256 
257   if (StyleDisplay()->IsScrollableOverflow()) {
258     float x, y, width, height;
259     static_cast<nsSVGElement*>(mContent)->
260       GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
261 
262     gfxRect clipRect =
263       nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height);
264     nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
265   }
266 
267   // SVG paints in CSS px, but normally frames paint in dev pixels. Here we
268   // multiply a CSS-px-to-dev-pixel factor onto aTransform so our children
269   // paint correctly.
270   float cssPxPerDevPx = PresContext()->
271     AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel());
272   gfxMatrix canvasTMForChildren = aTransform;
273   canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx);
274 
275   aContext.Multiply(canvasTMForChildren);
276 
277   using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
278   PaintFrameFlags flags = PaintFrameFlags::PAINT_IN_TRANSFORM;
279   if (SVGAutoRenderState::IsPaintingToWindow(aContext.GetDrawTarget())) {
280     flags |= PaintFrameFlags::PAINT_TO_WINDOW;
281   }
282   nsRenderingContext rendCtx(&aContext);
283   nsresult rv = nsLayoutUtils::PaintFrame(&rendCtx, kid, nsRegion(kidDirtyRect),
284                                          NS_RGBA(0,0,0,0),
285                                          nsDisplayListBuilderMode::PAINTING,
286                                          flags);
287 
288   aContext.Restore();
289 
290   return NS_FAILED(rv) ? DrawResult::BAD_ARGS : DrawResult::SUCCESS;
291 }
292 
293 nsIFrame*
GetFrameForPoint(const gfxPoint & aPoint)294 nsSVGForeignObjectFrame::GetFrameForPoint(const gfxPoint& aPoint)
295 {
296   NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
297                (mState & NS_FRAME_IS_NONDISPLAY),
298                "If display lists are enabled, only hit-testing of a "
299                "clipPath's contents should take this code path");
300 
301   if (IsDisabled() || (GetStateBits() & NS_FRAME_IS_NONDISPLAY))
302     return nullptr;
303 
304   nsIFrame* kid = PrincipalChildList().FirstChild();
305   if (!kid)
306     return nullptr;
307 
308   float x, y, width, height;
309   static_cast<nsSVGElement*>(mContent)->
310     GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
311 
312   if (!gfxRect(x, y, width, height).Contains(aPoint) ||
313       !nsSVGUtils::HitTestClip(this, aPoint)) {
314     return nullptr;
315   }
316 
317   // Convert the point to app units relative to the top-left corner of the
318   // viewport that's established by the foreignObject element:
319 
320   gfxPoint pt = (aPoint + gfxPoint(x, y)) * nsPresContext::AppUnitsPerCSSPixel();
321   nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y));
322 
323   return nsLayoutUtils::GetFrameForPoint(kid, point);
324 }
325 
326 nsRect
GetCoveredRegion()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(),
337                                     PresContext());
338 }
339 
340 void
ReflowSVG()341 nsSVGForeignObjectFrame::ReflowSVG()
342 {
343   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
344                "This call is probably a wasteful mistake");
345 
346   MOZ_ASSERT(!(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 = PrincipalChildList().FirstChild();
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 (StyleEffects()->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
NotifySVGChanged(uint32_t aFlags)403 nsSVGForeignObjectFrame::NotifySVGChanged(uint32_t aFlags)
404 {
405   MOZ_ASSERT(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
GetBBoxContribution(const Matrix & aToBBoxUserspace,uint32_t aFlags)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
GetCanvasTM()495 nsSVGForeignObjectFrame::GetCanvasTM()
496 {
497   if (!mCanvasTM) {
498     NS_ASSERTION(GetParent(), "null parent");
499 
500     nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
501     SVGForeignObjectElement *content =
502       static_cast<SVGForeignObjectElement*>(mContent);
503 
504     gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
505 
506     mCanvasTM = new gfxMatrix(tm);
507   }
508   return *mCanvasTM;
509 }
510 
511 //----------------------------------------------------------------------
512 // Implementation helpers
513 
RequestReflow(nsIPresShell::IntrinsicDirty aType)514 void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType)
515 {
516   if (GetStateBits() & NS_FRAME_FIRST_REFLOW)
517     // If we haven't had a ReflowSVG() yet, nothing to do.
518     return;
519 
520   nsIFrame* kid = PrincipalChildList().FirstChild();
521   if (!kid)
522     return;
523 
524   PresContext()->PresShell()->FrameNeedsReflow(kid, aType, NS_FRAME_IS_DIRTY);
525 }
526 
527 void
DoReflow()528 nsSVGForeignObjectFrame::DoReflow()
529 {
530   MarkInReflow();
531   // Skip reflow if we're zero-sized, unless this is our first reflow.
532   if (IsDisabled() &&
533       !(GetStateBits() & NS_FRAME_FIRST_REFLOW))
534     return;
535 
536   nsPresContext *presContext = PresContext();
537   nsIFrame* kid = PrincipalChildList().FirstChild();
538   if (!kid)
539     return;
540 
541   // initiate a synchronous reflow here and now:
542   nsRenderingContext renderingContext(
543     presContext->PresShell()->CreateReferenceRenderingContext());
544 
545   mInReflow = true;
546 
547   WritingMode wm = kid->GetWritingMode();
548   ReflowInput reflowInput(presContext, kid,
549                                 &renderingContext,
550                                 LogicalSize(wm, ISize(wm),
551                                             NS_UNCONSTRAINEDSIZE));
552   ReflowOutput desiredSize(reflowInput);
553   nsReflowStatus status;
554 
555   // We don't use mRect.height above because that tells the child to do
556   // page/column breaking at that height.
557   NS_ASSERTION(reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) &&
558                reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
559                "style system should ensure that :-moz-svg-foreign-content "
560                "does not get styled");
561   NS_ASSERTION(reflowInput.ComputedISize() == ISize(wm),
562                "reflow state made child wrong size");
563   reflowInput.SetComputedBSize(BSize(wm));
564 
565   ReflowChild(kid, presContext, desiredSize, reflowInput, 0, 0,
566               NS_FRAME_NO_MOVE_FRAME, status);
567   NS_ASSERTION(mRect.width == desiredSize.Width() &&
568                mRect.height == desiredSize.Height(), "unexpected size");
569   FinishReflowChild(kid, presContext, desiredSize, &reflowInput, 0, 0,
570                     NS_FRAME_NO_MOVE_FRAME);
571 
572   mInReflow = false;
573 }
574 
575 nsRect
GetInvalidRegion()576 nsSVGForeignObjectFrame::GetInvalidRegion()
577 {
578   MOZ_ASSERT(!NS_SVGDisplayListPaintingEnabled(),
579              "Only called by nsDisplayOuterSVG code");
580 
581   nsIFrame* kid = PrincipalChildList().FirstChild();
582   if (kid->HasInvalidFrameInSubtree()) {
583     gfxRect r(mRect.x, mRect.y, mRect.width, mRect.height);
584     r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
585     nsRect rect = nsSVGUtils::ToCanvasBounds(r, GetCanvasTM(), PresContext());
586     rect = nsSVGUtils::GetPostFilterVisualOverflowRect(this, rect);
587     return rect;
588   }
589   return nsRect();
590 }
591 
592 
593