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