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